<commit_before>/*
 ***********************************************************************************************************************
 *
 *  Copyright (c) 2019-2021 Advanced Micro Devices, Inc. All Rights Reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 **********************************************************************************************************************/
/**
 ***********************************************************************************************************************
 * @file  LgcContext.cpp
 * @brief LLPC source file: implementation of llpc::LgcContext class for creating and using lgc::Builder
 ***********************************************************************************************************************
 */
#include "lgc/LgcContext.h"
#include "lgc/Builder.h"
#include "lgc/PassManager.h"
#include "lgc/patch/Patch.h"
#include "lgc/state/PassManagerCache.h"
#include "lgc/state/PipelineState.h"
#include "lgc/state/TargetInfo.h"
#include "lgc/util/Debug.h"
#include "lgc/util/Internal.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"

#define DEBUG_TYPE "lgc-context"

namespace llvm {

namespace cl {

// Set the optimization level
extern opt<CodeGenOpt::Level> OptLevel;

} // namespace cl
} // namespace llvm

using namespace lgc;
using namespace llvm;

namespace llvm {
void initializeBuilderReplayerPass(PassRegistry &);
} // namespace llvm

static codegen::RegisterCodeGenFlags CGF;

#ifndef NDEBUG
static bool Initialized;
#endif

raw_ostream *LgcContext::m_llpcOuts;

// -emit-llvm: emit LLVM assembly instead of ISA
static cl::opt<bool> EmitLlvm("emit-llvm", cl::desc("Emit LLVM assembly instead of AMD GPU ISA"), cl::init(false));

// -emit-llvm-bc: emit LLVM bitcode instead of ISA
static cl::opt<bool> EmitLlvmBc("emit-llvm-bc", cl::desc("Emit LLVM bitcode instead of AMD GPU ISA"), cl::init(false));

// -emit-lgc: emit LLVM assembly suitable for input to LGC (middle-end compiler)
static cl::opt<bool> EmitLgc("emit-lgc", cl::desc("Emit LLVM assembly suitable for input to LGC (middle-end compiler)"),
                             cl::init(false));

// -show-encoding: show the instruction encoding when emitting assembler. This mirrors llvm-mc behaviour
static cl::opt<bool> ShowEncoding("show-encoding", cl::desc("Show instruction encodings"), cl::init(false));

// =====================================================================================================================
// Set default for a command-line option, but only if command-line processing has not happened yet, or did not see
// an occurrence of this option.
//
// @param name : Option name
// @param value : Default option value
static void setOptionDefault(const char *name, StringRef value) {
  auto optIterator = cl::getRegisteredOptions().find(name);
  assert(optIterator != cl::getRegisteredOptions().end() && "Failed to find option to set default");
  cl::Option *opt = optIterator->second;
  if (opt->getNumOccurrences())
    return;
  // Setting MultiArg means that addOccurrence will not increment the option's occurrence count, so the user
  // can still specify it to override our default here.
  bool setFailed = opt->addOccurrence(0, opt->ArgStr, value, /*MultiArg=*/true);
  assert(!setFailed && "Failed to set default for option");
  ((void)setFailed);
}

// =====================================================================================================================
// Initialize the middle-end. This must be called before the first LgcContext::Create, although you are
// allowed to call it again after that. It must also be called before LLVM command-line processing, so
// that you can use a pass name in an option such as -print-after. If multiple concurrent compiles are
// possible, this should be called in a thread-safe way.
void LgcContext::initialize() {
#ifndef NDEBUG
  Initialized = true;
#endif

  auto &passRegistry = *PassRegistry::getPassRegistry();

  // Initialize LLVM target: AMDGPU
  LLVMInitializeAMDGPUTargetInfo();
  LLVMInitializeAMDGPUTarget();
  LLVMInitializeAMDGPUTargetMC();
  LLVMInitializeAMDGPUAsmPrinter();
  LLVMInitializeAMDGPUAsmParser();
  LLVMInitializeAMDGPUDisassembler();

  // Initialize core LLVM passes so they can be referenced by -stop-before etc.
  initializeCore(passRegistry);
  initializeTransformUtils(passRegistry);
  initializeScalarOpts(passRegistry);
  initializeVectorization(passRegistry);
  initializeInstCombine(passRegistry);
  initializeAggressiveInstCombine(passRegistry);
  initializeIPO(passRegistry);
  initializeCodeGen(passRegistry);
  initializeShadowStackGCLoweringPass(passRegistry);
  initializeExpandReductionsPass(passRegistry);
  initializeRewriteSymbolsLegacyPassPass(passRegistry);

  // Initialize LGC passes so they can be referenced by -stop-before etc.
  initializeUtilPasses(passRegistry);
  initializeStatePasses(passRegistry);
  initializeBuilderReplayerPass(passRegistry);
  initializePatchPasses(passRegistry);

  // Initialize some command-line option defaults.
  setOptionDefault("filetype", "obj");
  setOptionDefault("amdgpu-unroll-max-block-to-analyze", "20");
  setOptionDefault("unroll-max-percent-threshold-boost", "1000");
  setOptionDefault("unroll-allow-partial", "1");
  // TODO: phi-of-ops optimization in NewGVN has some problems, we temporarily
  // disable this to avoid mis-compile, see (https://github.com/GPUOpen-Drivers/llpc/issues/1206).
  setOptionDefault("enable-phi-of-ops", "0");
  setOptionDefault("simplifycfg-sink-common", "0");
  setOptionDefault("amdgpu-vgpr-index-mode", "1"); // force VGPR indexing on GFX8
  setOptionDefault("amdgpu-atomic-optimizations", "1");
  setOptionDefault("use-gpu-divergence-analysis", "1");
  setOptionDefault("structurizecfg-skip-uniform-regions", "1");
  setOptionDefault("spec-exec-max-speculation-cost", "10");
#if !defined(LLVM_HAVE_BRANCH_AMD_GFX)
#warning[!amd-gfx] Conditional discard transformations not supported
#else
  setOptionDefault("amdgpu-conditional-discard-transformations", "1");
#endif
}

// =====================================================================================================================
// Create the LgcContext. Returns nullptr on failure to recognize the AMDGPU target whose name is specified
//
// @param context : LLVM context to give each Builder
// @param gpuName : LLVM GPU name (e.g. "gfx900"); empty to use -mcpu option setting
// @param palAbiVersion : PAL pipeline ABI version to compile for
LgcContext *LgcContext::Create(LLVMContext &context, StringRef gpuName, unsigned palAbiVersion) {
  assert(Initialized && "Must call LgcContext::Initialize before LgcContext::Create");

  LgcContext *builderContext = new LgcContext(context, palAbiVersion);

  std::string mcpuName = codegen::getMCPU(); // -mcpu setting from llvm/CodeGen/CommandFlags.h
  if (gpuName == "")
    gpuName = mcpuName;

  builderContext->m_targetInfo = new TargetInfo;
  if (!builderContext->m_targetInfo->setTargetInfo(gpuName)) {
    delete builderContext;
    return nullptr;
  }

  // Get the LLVM target and create the target machine. This should not fail, as we determined above
  // that we support the requested target.
  const std::string triple = "amdgcn--amdpal";
  std::string errMsg;
  const Target *target = TargetRegistry::lookupTarget(triple, errMsg);
  // Allow no signed zeros - this enables omod modifiers (div:2, mul:2)
  TargetOptions targetOpts;
  targetOpts.NoSignedZerosFPMath = true;

  // Enable instruction encoding output - outputs hex in comment mirroring
  // llvm-mc behaviour
  if (ShowEncoding) {
    targetOpts.MCOptions.ShowMCEncoding = true;
    targetOpts.MCOptions.AsmVerbose = true;
  }

  LLPC_OUTS("TargetMachine optimization level = " << cl::OptLevel << "\n");

  builderContext->m_targetMachine =
      target->createTargetMachine(triple, gpuName, "", targetOpts, Optional<Reloc::Model>(), None, cl::OptLevel);
  assert(builderContext->m_targetMachine);
  return builderContext;
}

// =====================================================================================================================
//
// @param context : LLVM context to give each Builder
// @param palAbiVersion : PAL pipeline ABI version to compile for
LgcContext::LgcContext(LLVMContext &context, unsigned palAbiVersion) : m_context(context) {
}

// =====================================================================================================================
LgcContext::~LgcContext() {
  delete m_targetMachine;
  delete m_targetInfo;
  delete m_passManagerCache;
}

// =====================================================================================================================
// Create a Pipeline object for a pipeline compile.
// This actually creates a PipelineState, but returns the Pipeline superclass that is visible to
// the front-end.
Pipeline *LgcContext::createPipeline() {
  return new PipelineState(this, EmitLgc);
}

// =====================================================================================================================
// Create a Builder object. For a shader compile (pPipeline is nullptr), useBuilderRecorder is ignored
// because it always uses BuilderRecorder.
//
// @param pipeline : Pipeline object for pipeline compile, nullptr for shader compile
// @param useBuilderRecorder : True to use BuilderRecorder, false to use BuilderImpl
Builder *LgcContext::createBuilder(Pipeline *pipeline, bool useBuilderRecorder) {
  if (!pipeline || useBuilderRecorder || EmitLgc)
    return Builder::createBuilderRecorder(this, pipeline, EmitLgc);
  return Builder::createBuilderImpl(this, pipeline);
}

// =====================================================================================================================
// Prepare a pass manager. This manually adds a target-aware TLI pass, so middle-end optimizations do not think that
// we have library functions.
//
// @param [in/out] passMgr : Pass manager
void LgcContext::preparePassManager(legacy::PassManager *passMgr) {
  TargetLibraryInfoImpl targetLibInfo(getTargetMachine()->getTargetTriple());

  // Adjust it to allow memcpy and memset.
  // TODO: Investigate why the latter is necessary. I found that
  // test/shaderdb/ObjStorageBlock_TestMemCpyInt32.comp
  // got unrolled far too much, and at too late a stage for the descriptor loads to be commoned up. It might
  // be an unfortunate interaction between LoopIdiomRecognize and fat pointer laundering.
  targetLibInfo.setAvailable(LibFunc_memcpy);
  targetLibInfo.setAvailable(LibFunc_memset);

  auto targetLibInfoPass = new TargetLibraryInfoWrapperPass(targetLibInfo);
  passMgr->add(targetLibInfoPass);
}

// =====================================================================================================================
// Adds target passes to pass manager, depending on "-filetype" and "-emit-llvm" options
//
// @param [in/out] passMgr : Pass manager to add passes to
// @param codeGenTimer : Timer to time target passes with, nullptr if not timing
// @param [out] outStream : Output stream
void LgcContext::addTargetPasses(lgc::PassManager &passMgr, Timer *codeGenTimer, raw_pwrite_stream &outStream) {
  // Start timer for codegen passes.
  if (codeGenTimer)
    passMgr.add(createStartStopTimer(codeGenTimer, true));

  // Dump the module just before codegen.
  if (raw_ostream *outs = getLgcOuts()) {
    passMgr.add(
        createPrintModulePass(*outs, "===============================================================================\n"
                                     "// LLPC final pipeline module info\n"));
  }

  if (EmitLlvm && EmitLlvmBc)
    report_fatal_error("-emit-llvm conflicts with -emit-llvm-bc");

  if (EmitLlvm) {
    // For -emit-llvm, add a pass to output the LLVM IR, then tell the pass manager to stop adding
    // passes. We do it this way to ensure that we still get the immutable passes from
    // TargetMachine::addPassesToEmitFile, as they can affect LLVM middle-end optimizations.
    passMgr.add(createPrintModulePass(outStream));
    passMgr.stop();
  }

  if (EmitLlvmBc) {
    // For -emit-llvm-bc, add a pass to output the LLVM IR, then tell the pass manager to stop adding
    // passes. We do it this way to ensure that we still get the immutable passes from
    // TargetMachine::addPassesToEmitFile, as they can affect LLVM middle-end optimizations.
    passMgr.add(createBitcodeWriterPass(outStream));
    passMgr.stop();
  }

  // TODO: We should probably be using InitTargetOptionsFromCodeGenFlags() here.
  // Currently we are not, and it would give an "unused function" warning when compiled with
  // CLANG. So we avoid the warning by referencing it here.
  (void(&codegen::InitTargetOptionsFromCodeGenFlags)); // unused

  if (getTargetMachine()->addPassesToEmitFile(passMgr, outStream, nullptr, codegen::getFileType()))
    report_fatal_error("Target machine cannot emit a file of this type");

  // Stop timer for codegen passes.
  if (codeGenTimer)
    passMgr.add(createStartStopTimer(codeGenTimer, false));
}

// =====================================================================================================================
// Get pass manager cache
PassManagerCache *LgcContext::getPassManagerCache() {
  if (!m_passManagerCache)
    m_passManagerCache = new PassManagerCache(this);
  return m_passManagerCache;
}
<commit_msg>Revert "Disable phi-of-op optimization in NewGVN"<commit_after>/*
 ***********************************************************************************************************************
 *
 *  Copyright (c) 2019-2021 Advanced Micro Devices, Inc. All Rights Reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 **********************************************************************************************************************/
/**
 ***********************************************************************************************************************
 * @file  LgcContext.cpp
 * @brief LLPC source file: implementation of llpc::LgcContext class for creating and using lgc::Builder
 ***********************************************************************************************************************
 */
#include "lgc/LgcContext.h"
#include "lgc/Builder.h"
#include "lgc/PassManager.h"
#include "lgc/patch/Patch.h"
#include "lgc/state/PassManagerCache.h"
#include "lgc/state/PipelineState.h"
#include "lgc/state/TargetInfo.h"
#include "lgc/util/Debug.h"
#include "lgc/util/Internal.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"

#define DEBUG_TYPE "lgc-context"

namespace llvm {

namespace cl {

// Set the optimization level
extern opt<CodeGenOpt::Level> OptLevel;

} // namespace cl
} // namespace llvm

using namespace lgc;
using namespace llvm;

namespace llvm {
void initializeBuilderReplayerPass(PassRegistry &);
} // namespace llvm

static codegen::RegisterCodeGenFlags CGF;

#ifndef NDEBUG
static bool Initialized;
#endif

raw_ostream *LgcContext::m_llpcOuts;

// -emit-llvm: emit LLVM assembly instead of ISA
static cl::opt<bool> EmitLlvm("emit-llvm", cl::desc("Emit LLVM assembly instead of AMD GPU ISA"), cl::init(false));

// -emit-llvm-bc: emit LLVM bitcode instead of ISA
static cl::opt<bool> EmitLlvmBc("emit-llvm-bc", cl::desc("Emit LLVM bitcode instead of AMD GPU ISA"), cl::init(false));

// -emit-lgc: emit LLVM assembly suitable for input to LGC (middle-end compiler)
static cl::opt<bool> EmitLgc("emit-lgc", cl::desc("Emit LLVM assembly suitable for input to LGC (middle-end compiler)"),
                             cl::init(false));

// -show-encoding: show the instruction encoding when emitting assembler. This mirrors llvm-mc behaviour
static cl::opt<bool> ShowEncoding("show-encoding", cl::desc("Show instruction encodings"), cl::init(false));

// =====================================================================================================================
// Set default for a command-line option, but only if command-line processing has not happened yet, or did not see
// an occurrence of this option.
//
// @param name : Option name
// @param value : Default option value
static void setOptionDefault(const char *name, StringRef value) {
  auto optIterator = cl::getRegisteredOptions().find(name);
  assert(optIterator != cl::getRegisteredOptions().end() && "Failed to find option to set default");
  cl::Option *opt = optIterator->second;
  if (opt->getNumOccurrences())
    return;
  // Setting MultiArg means that addOccurrence will not increment the option's occurrence count, so the user
  // can still specify it to override our default here.
  bool setFailed = opt->addOccurrence(0, opt->ArgStr, value, /*MultiArg=*/true);
  assert(!setFailed && "Failed to set default for option");
  ((void)setFailed);
}

// =====================================================================================================================
// Initialize the middle-end. This must be called before the first LgcContext::Create, although you are
// allowed to call it again after that. It must also be called before LLVM command-line processing, so
// that you can use a pass name in an option such as -print-after. If multiple concurrent compiles are
// possible, this should be called in a thread-safe way.
void LgcContext::initialize() {
#ifndef NDEBUG
  Initialized = true;
#endif

  auto &passRegistry = *PassRegistry::getPassRegistry();

  // Initialize LLVM target: AMDGPU
  LLVMInitializeAMDGPUTargetInfo();
  LLVMInitializeAMDGPUTarget();
  LLVMInitializeAMDGPUTargetMC();
  LLVMInitializeAMDGPUAsmPrinter();
  LLVMInitializeAMDGPUAsmParser();
  LLVMInitializeAMDGPUDisassembler();

  // Initialize core LLVM passes so they can be referenced by -stop-before etc.
  initializeCore(passRegistry);
  initializeTransformUtils(passRegistry);
  initializeScalarOpts(passRegistry);
  initializeVectorization(passRegistry);
  initializeInstCombine(passRegistry);
  initializeAggressiveInstCombine(passRegistry);
  initializeIPO(passRegistry);
  initializeCodeGen(passRegistry);
  initializeShadowStackGCLoweringPass(passRegistry);
  initializeExpandReductionsPass(passRegistry);
  initializeRewriteSymbolsLegacyPassPass(passRegistry);

  // Initialize LGC passes so they can be referenced by -stop-before etc.
  initializeUtilPasses(passRegistry);
  initializeStatePasses(passRegistry);
  initializeBuilderReplayerPass(passRegistry);
  initializePatchPasses(passRegistry);

  // Initialize some command-line option defaults.
  setOptionDefault("filetype", "obj");
  setOptionDefault("amdgpu-unroll-max-block-to-analyze", "20");
  setOptionDefault("unroll-max-percent-threshold-boost", "1000");
  setOptionDefault("unroll-allow-partial", "1");
  setOptionDefault("simplifycfg-sink-common", "0");
  setOptionDefault("amdgpu-vgpr-index-mode", "1"); // force VGPR indexing on GFX8
  setOptionDefault("amdgpu-atomic-optimizations", "1");
  setOptionDefault("use-gpu-divergence-analysis", "1");
  setOptionDefault("structurizecfg-skip-uniform-regions", "1");
  setOptionDefault("spec-exec-max-speculation-cost", "10");
#if !defined(LLVM_HAVE_BRANCH_AMD_GFX)
#warning[!amd-gfx] Conditional discard transformations not supported
#else
  setOptionDefault("amdgpu-conditional-discard-transformations", "1");
#endif
}

// =====================================================================================================================
// Create the LgcContext. Returns nullptr on failure to recognize the AMDGPU target whose name is specified
//
// @param context : LLVM context to give each Builder
// @param gpuName : LLVM GPU name (e.g. "gfx900"); empty to use -mcpu option setting
// @param palAbiVersion : PAL pipeline ABI version to compile for
LgcContext *LgcContext::Create(LLVMContext &context, StringRef gpuName, unsigned palAbiVersion) {
  assert(Initialized && "Must call LgcContext::Initialize before LgcContext::Create");

  LgcContext *builderContext = new LgcContext(context, palAbiVersion);

  std::string mcpuName = codegen::getMCPU(); // -mcpu setting from llvm/CodeGen/CommandFlags.h
  if (gpuName == "")
    gpuName = mcpuName;

  builderContext->m_targetInfo = new TargetInfo;
  if (!builderContext->m_targetInfo->setTargetInfo(gpuName)) {
    delete builderContext;
    return nullptr;
  }

  // Get the LLVM target and create the target machine. This should not fail, as we determined above
  // that we support the requested target.
  const std::string triple = "amdgcn--amdpal";
  std::string errMsg;
  const Target *target = TargetRegistry::lookupTarget(triple, errMsg);
  // Allow no signed zeros - this enables omod modifiers (div:2, mul:2)
  TargetOptions targetOpts;
  targetOpts.NoSignedZerosFPMath = true;

  // Enable instruction encoding output - outputs hex in comment mirroring
  // llvm-mc behaviour
  if (ShowEncoding) {
    targetOpts.MCOptions.ShowMCEncoding = true;
    targetOpts.MCOptions.AsmVerbose = true;
  }

  LLPC_OUTS("TargetMachine optimization level = " << cl::OptLevel << "\n");

  builderContext->m_targetMachine =
      target->createTargetMachine(triple, gpuName, "", targetOpts, Optional<Reloc::Model>(), None, cl::OptLevel);
  assert(builderContext->m_targetMachine);
  return builderContext;
}

// =====================================================================================================================
//
// @param context : LLVM context to give each Builder
// @param palAbiVersion : PAL pipeline ABI version to compile for
LgcContext::LgcContext(LLVMContext &context, unsigned palAbiVersion) : m_context(context) {
}

// =====================================================================================================================
LgcContext::~LgcContext() {
  delete m_targetMachine;
  delete m_targetInfo;
  delete m_passManagerCache;
}

// =====================================================================================================================
// Create a Pipeline object for a pipeline compile.
// This actually creates a PipelineState, but returns the Pipeline superclass that is visible to
// the front-end.
Pipeline *LgcContext::createPipeline() {
  return new PipelineState(this, EmitLgc);
}

// =====================================================================================================================
// Create a Builder object. For a shader compile (pPipeline is nullptr), useBuilderRecorder is ignored
// because it always uses BuilderRecorder.
//
// @param pipeline : Pipeline object for pipeline compile, nullptr for shader compile
// @param useBuilderRecorder : True to use BuilderRecorder, false to use BuilderImpl
Builder *LgcContext::createBuilder(Pipeline *pipeline, bool useBuilderRecorder) {
  if (!pipeline || useBuilderRecorder || EmitLgc)
    return Builder::createBuilderRecorder(this, pipeline, EmitLgc);
  return Builder::createBuilderImpl(this, pipeline);
}

// =====================================================================================================================
// Prepare a pass manager. This manually adds a target-aware TLI pass, so middle-end optimizations do not think that
// we have library functions.
//
// @param [in/out] passMgr : Pass manager
void LgcContext::preparePassManager(legacy::PassManager *passMgr) {
  TargetLibraryInfoImpl targetLibInfo(getTargetMachine()->getTargetTriple());

  // Adjust it to allow memcpy and memset.
  // TODO: Investigate why the latter is necessary. I found that
  // test/shaderdb/ObjStorageBlock_TestMemCpyInt32.comp
  // got unrolled far too much, and at too late a stage for the descriptor loads to be commoned up. It might
  // be an unfortunate interaction between LoopIdiomRecognize and fat pointer laundering.
  targetLibInfo.setAvailable(LibFunc_memcpy);
  targetLibInfo.setAvailable(LibFunc_memset);

  auto targetLibInfoPass = new TargetLibraryInfoWrapperPass(targetLibInfo);
  passMgr->add(targetLibInfoPass);
}

// =====================================================================================================================
// Adds target passes to pass manager, depending on "-filetype" and "-emit-llvm" options
//
// @param [in/out] passMgr : Pass manager to add passes to
// @param codeGenTimer : Timer to time target passes with, nullptr if not timing
// @param [out] outStream : Output stream
void LgcContext::addTargetPasses(lgc::PassManager &passMgr, Timer *codeGenTimer, raw_pwrite_stream &outStream) {
  // Start timer for codegen passes.
  if (codeGenTimer)
    passMgr.add(createStartStopTimer(codeGenTimer, true));

  // Dump the module just before codegen.
  if (raw_ostream *outs = getLgcOuts()) {
    passMgr.add(
        createPrintModulePass(*outs, "===============================================================================\n"
                                     "// LLPC final pipeline module info\n"));
  }

  if (EmitLlvm && EmitLlvmBc)
    report_fatal_error("-emit-llvm conflicts with -emit-llvm-bc");

  if (EmitLlvm) {
    // For -emit-llvm, add a pass to output the LLVM IR, then tell the pass manager to stop adding
    // passes. We do it this way to ensure that we still get the immutable passes from
    // TargetMachine::addPassesToEmitFile, as they can affect LLVM middle-end optimizations.
    passMgr.add(createPrintModulePass(outStream));
    passMgr.stop();
  }

  if (EmitLlvmBc) {
    // For -emit-llvm-bc, add a pass to output the LLVM IR, then tell the pass manager to stop adding
    // passes. We do it this way to ensure that we still get the immutable passes from
    // TargetMachine::addPassesToEmitFile, as they can affect LLVM middle-end optimizations.
    passMgr.add(createBitcodeWriterPass(outStream));
    passMgr.stop();
  }

  // TODO: We should probably be using InitTargetOptionsFromCodeGenFlags() here.
  // Currently we are not, and it would give an "unused function" warning when compiled with
  // CLANG. So we avoid the warning by referencing it here.
  (void(&codegen::InitTargetOptionsFromCodeGenFlags)); // unused

  if (getTargetMachine()->addPassesToEmitFile(passMgr, outStream, nullptr, codegen::getFileType()))
    report_fatal_error("Target machine cannot emit a file of this type");

  // Stop timer for codegen passes.
  if (codeGenTimer)
    passMgr.add(createStartStopTimer(codeGenTimer, false));
}

// =====================================================================================================================
// Get pass manager cache
PassManagerCache *LgcContext::getPassManagerCache() {
  if (!m_passManagerCache)
    m_passManagerCache = new PassManagerCache(this);
  return m_passManagerCache;
}
<|endoftext|>


<commit_before>#include "raven_sqlite.h"

#include <cstdio>
#include <stdarg.h>
#include <cstring>

using std::string;
using std::vector;
using std::wstring;

#ifdef _mingw_
#include <cstring>
#define vswprintf_s vsnwprintf
#define vsprintf_s vsnprintf
#pragma GCC diagnostic ignored "-Wwrite-strings"
#endif

namespace raven
{
	namespace sqlite
	{

		/**

		Open database file UTF16

		@param[in] fname

		*/
		void cDB::Open(const wchar_t *fname)
		{
			if (db)
			{
				myError = "ERROR: db already open";
				return;
			}
			int ret = sqlite3_open16(fname, &db);
			if (ret)
			{
				myError = "ERROR: failed to open db";
				return;
			}
			if (!db)
			{
				myError = "ERROR: failed to open db";
				return;
			}
			myError = 0;
		}
		/**

		Open database file UTF8

		@param[in] fname

		*/
		void cDB::Open(const char *fname)
		{
			if (db)
			{
				myError = "ERROR: db already open";
				return;
			}
			int ret = sqlite3_open(fname, &db);
			if (ret)
			{
				myError = "ERROR: failed to open db";
				return;
			}
			if (!db)
			{
				myError = "ERROR: failed to open db";
				return;
			}
			myError = 0;
		}

		void cDB::Close()
		{
			if (db)
				sqlite3_close(db);
			db = 0;
		}

		cDB::~cDB()
		{
			Close();
		}

		int cDB::Query(const wchar_t *f, ...)
		{
			if (!db)
			{
				myError = "ERROR: db not open";
				return -1;
			}

			va_list _Arglist;
			va_start(_Arglist, f);
			int r;

			// clear results
			myResult.clear();
			myError = 0;

			// format query
			r = vswprintf_s(queryformatted, 999, f, _Arglist);
			va_end(_Arglist);
			if (r < 1)
			{
				myError = "Query string too long for buffer";
				return -1;
			}

			// submit query - just one
			sqlite3_stmt *st = 0;
			int ret;
			void const *tail = 0;
			ret = sqlite3_prepare16_v2(db, queryformatted, -1, &st, &tail);
			if (ret != SQLITE_OK)
			{
				// error in query - provide human readable msg
				int nErrMsg = 1 + (int)strlen(sqlite3_errmsg(db));
				myError = (char *)sqlite3_malloc(nErrMsg);
				if (myError)
				{
					memcpy((void *)myError, sqlite3_errmsg(db), nErrMsg);
				}
				else
				{
					myError = "out of memory";
				}
				return -1;
			}

			if (sqlite3_step(st) != SQLITE_ROW)
			{
				// no data returned, so finalize
				sqlite3_finalize(st);
				return 0;
			}

			// check for some results
			if (!st)
				return 0;
			myColCount = sqlite3_column_count(st);
			if (!myColCount)
			{
				return 0;
			}

			int row_count = 0;
			// loop over rows returned
			do
			{
				// loop over columns
				for (int kcol = 0; kcol < myColCount; kcol++)
				{
					// check for null value
					if (sqlite3_column_type(st, kcol) == SQLITE_NULL)
						myResult.push_back(wstring(L""));
					else
						myResult.push_back(wstring((wchar_t *)sqlite3_column_text16(st, kcol)));
				}
				row_count++;
			} while (sqlite3_step(st) == SQLITE_ROW);

			// clean up
			sqlite3_finalize(st);

			return row_count;
		}
		int cDB::Query(const char *f, ...)
		{
			if (!db)
			{
				myError = "ERROR: db not open";
				return -1;
			}

			va_list _Arglist;
			va_start(_Arglist, f);
			int r;

			// clear results
			myResultA.clear();
			myError = 0;

			// format query
			r = vsprintf_s(queryformattedA, 1999, f, _Arglist);
			va_end(_Arglist);
			if (r < 1)
			{
				myError = "Query string too long for buffer";
				return -1;
			}

			// submit query - just one
			sqlite3_stmt *st = 0;
			int ret;
			char const *tail = 0;
			ret = sqlite3_prepare_v2(db, queryformattedA, -1, &st, &tail);
			if (ret != SQLITE_OK)
			{
				// error in query - provide human readable msg
				int nErrMsg = 1 + (int)strlen(sqlite3_errmsg(db));
				myError = (char *)sqlite3_malloc(nErrMsg);
				if (myError)
				{
					memcpy((void *)myError, sqlite3_errmsg(db), nErrMsg);
				}
				else
				{
					myError = "out of memory";
				}
				return -1;
			}

			if (sqlite3_step(st) != SQLITE_ROW)
			{
				// no data returned, so finalize
				sqlite3_finalize(st);
				return 0;
			}

			// check for some results
			if (!st)
				return 0;
			myColCount = sqlite3_column_count(st);
			if (!myColCount)
			{
				return 0;
			}

			int row_count = 0;
			// loop over rows returned
			do
			{
				// loop over columns
				for (int kcol = 0; kcol < myColCount; kcol++)
				{
					// check for null value
					if (sqlite3_column_type(st, kcol) == SQLITE_NULL)
						myResultA.push_back(string(""));
					else
						myResultA.push_back(string((char *)sqlite3_column_text(st, kcol)));
				}
				row_count++;
			} while (sqlite3_step(st) == SQLITE_ROW);

			// clean up
			sqlite3_finalize(st);

			return row_count;
		}
		sqlite3_stmt *cDB::Prepare(
			const std::string &f)
		{
			stmt = 0;
			const char *tail = 0;
			int ret = sqlite3_prepare_v2(
				db,
				f.c_str(),
				-1,
				&stmt,
				&tail);
			if (ret != SQLITE_OK)
			{
				stmt = 0;
				decodeError();
				return stmt;
			}
			return stmt;
		}
		int cDB::Bind(int index, int value)
		{
			if (!stmt)
			{
				myError = "no compiled statement";
				return -1;
			}
			if (sqlite3_bind_int(stmt, index, value))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		int cDB::Bind(int index, double value)
		{
			if (!stmt)
			{
				myError = "no compiled statement";
				return -1;
			}
			if (sqlite3_bind_double(stmt, index, value))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		int cDB::Bind(int index, void *p, int size)
		{
			if (!stmt)
			{
				myError = "no compiled statement";
				return -1;
			}
			if (sqlite3_bind_blob(stmt, index, p, size, SQLITE_STATIC))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		int cDB::Bind(int index, const std::string &str)
		{
			if (!stmt)
			{
				myError = "no compiled statement";
				return -1;
			}
			if (sqlite3_bind_text(stmt, index, str.c_str(), str.length(), SQLITE_STATIC))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		void cDB::decodeError()
		{
			int nErrMsg = 1 + (int)strlen(sqlite3_errmsg(db));
			myError = (char *)sqlite3_malloc(nErrMsg);
			if (myError)
			{
				memcpy((void *)myError, sqlite3_errmsg(db), nErrMsg);
			}
			else
			{
				myError = "out of memory";
			}
		}
	}
}
<commit_msg>Remove compiler warnings<commit_after>#include "raven_sqlite.h"

#include <cstdio>
#include <stdarg.h>
#include <cstring>

using std::string;
using std::vector;
using std::wstring;

#ifdef _mingw_
#include <cstring>
#define vswprintf_s vsnwprintf
#define vsprintf_s vsnprintf
#pragma GCC diagnostic ignored "-Wwrite-strings"
#endif

namespace raven
{
	namespace sqlite
	{

		/**

		Open database file UTF16

		@param[in] fname

		*/
		void cDB::Open(const wchar_t *fname)
		{
			if (db)
			{
				myError = (char*)"ERROR: db already open";
				return;
			}
			int ret = sqlite3_open16(fname, &db);
			if (ret)
			{
				myError = (char*)"ERROR: failed to open db";
				return;
			}
			if (!db)
			{
				myError = (char*)"ERROR: failed to open db";
				return;
			}
			myError = 0;
		}
		/**

		Open database file UTF8

		@param[in] fname

		*/
		void cDB::Open(const char *fname)
		{
			if (db)
			{
				myError = (char*)"ERROR: db already open";
				return;
			}
			int ret = sqlite3_open(fname, &db);
			if (ret)
			{
				myError = (char*)"ERROR: failed to open db";
				return;
			}
			if (!db)
			{
				myError = (char*)"ERROR: failed to open db";
				return;
			}
			myError = 0;
		}

		void cDB::Close()
		{
			if (db)
				sqlite3_close(db);
			db = 0;
		}

		cDB::~cDB()
		{
			Close();
		}

		int cDB::Query(const wchar_t *f, ...)
		{
			if (!db)
			{
				myError = (char*)"ERROR: db not open";
				return -1;
			}

			va_list _Arglist;
			va_start(_Arglist, f);
			int r;

			// clear results
			myResult.clear();
			myError = 0;

			// format query
			r = vswprintf_s(queryformatted, 999, f, _Arglist);
			va_end(_Arglist);
			if (r < 1)
			{
				myError = (char*)"Query string too long for buffer";
				return -1;
			}

			// submit query - just one
			sqlite3_stmt *st = 0;
			int ret;
			void const *tail = 0;
			ret = sqlite3_prepare16_v2(db, queryformatted, -1, &st, &tail);
			if (ret != SQLITE_OK)
			{
				// error in query - provide human readable msg
				int nErrMsg = 1 + (int)strlen(sqlite3_errmsg(db));
				myError = (char *)sqlite3_malloc(nErrMsg);
				if (myError)
				{
					memcpy((void *)myError, sqlite3_errmsg(db), nErrMsg);
				}
				else
				{
					myError = (char*)"out of memory";
				}
				return -1;
			}

			if (sqlite3_step(st) != SQLITE_ROW)
			{
				// no data returned, so finalize
				sqlite3_finalize(st);
				return 0;
			}

			// check for some results
			if (!st)
				return 0;
			myColCount = sqlite3_column_count(st);
			if (!myColCount)
			{
				return 0;
			}

			int row_count = 0;
			// loop over rows returned
			do
			{
				// loop over columns
				for (int kcol = 0; kcol < myColCount; kcol++)
				{
					// check for null value
					if (sqlite3_column_type(st, kcol) == SQLITE_NULL)
						myResult.push_back(wstring(L""));
					else
						myResult.push_back(wstring((wchar_t *)sqlite3_column_text16(st, kcol)));
				}
				row_count++;
			} while (sqlite3_step(st) == SQLITE_ROW);

			// clean up
			sqlite3_finalize(st);

			return row_count;
		}
		int cDB::Query(const char *f, ...)
		{
			if (!db)
			{
				myError = (char*)"ERROR: db not open";
				return -1;
			}

			va_list _Arglist;
			va_start(_Arglist, f);
			int r;

			// clear results
			myResultA.clear();
			myError = 0;

			// format query
			r = vsprintf_s(queryformattedA, 1999, f, _Arglist);
			va_end(_Arglist);
			if (r < 1)
			{
				myError = (char*)"Query string too long for buffer";
				return -1;
			}

			// submit query - just one
			sqlite3_stmt *st = 0;
			int ret;
			char const *tail = 0;
			ret = sqlite3_prepare_v2(db, queryformattedA, -1, &st, &tail);
			if (ret != SQLITE_OK)
			{
				// error in query - provide human readable msg
				int nErrMsg = 1 + (int)strlen(sqlite3_errmsg(db));
				myError = (char *)sqlite3_malloc(nErrMsg);
				if (myError)
				{
					memcpy((void *)myError, sqlite3_errmsg(db), nErrMsg);
				}
				else
				{
					myError = (char*)"out of memory";
				}
				return -1;
			}

			if (sqlite3_step(st) != SQLITE_ROW)
			{
				// no data returned, so finalize
				sqlite3_finalize(st);
				return 0;
			}

			// check for some results
			if (!st)
				return 0;
			myColCount = sqlite3_column_count(st);
			if (!myColCount)
			{
				return 0;
			}

			int row_count = 0;
			// loop over rows returned
			do
			{
				// loop over columns
				for (int kcol = 0; kcol < myColCount; kcol++)
				{
					// check for null value
					if (sqlite3_column_type(st, kcol) == SQLITE_NULL)
						myResultA.push_back(string(""));
					else
						myResultA.push_back(string((char *)sqlite3_column_text(st, kcol)));
				}
				row_count++;
			} while (sqlite3_step(st) == SQLITE_ROW);

			// clean up
			sqlite3_finalize(st);

			return row_count;
		}
		sqlite3_stmt *cDB::Prepare(
			const std::string &f)
		{
			stmt = 0;
			const char *tail = 0;
			int ret = sqlite3_prepare_v2(
				db,
				f.c_str(),
				-1,
				&stmt,
				&tail);
			if (ret != SQLITE_OK)
			{
				stmt = 0;
				decodeError();
				return stmt;
			}
			return stmt;
		}
		int cDB::Bind(int index, int value)
		{
			if (!stmt)
			{
				myError = (char*)"no compiled statement";
				return -1;
			}
			if (sqlite3_bind_int(stmt, index, value))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		int cDB::Bind(int index, double value)
		{
			if (!stmt)
			{
				myError = (char*)"no compiled statement";
				return -1;
			}
			if (sqlite3_bind_double(stmt, index, value))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		int cDB::Bind(int index, void *p, int size)
		{
			if (!stmt)
			{
				myError = (char*)"no compiled statement";
				return -1;
			}
			if (sqlite3_bind_blob(stmt, index, p, size, SQLITE_STATIC))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		int cDB::Bind(int index, const std::string &str)
		{
			if (!stmt)
			{
				myError = (char*)"no compiled statement";
				return -1;
			}
			if (sqlite3_bind_text(stmt, index, str.c_str(), str.length(), SQLITE_STATIC))
			{
				decodeError();
				return -1;
			}
			return 0;
		}
		void cDB::decodeError()
		{
			int nErrMsg = 1 + (int)strlen(sqlite3_errmsg(db));
			myError = (char *)sqlite3_malloc(nErrMsg);
			if (myError)
			{
				memcpy((void *)myError, sqlite3_errmsg(db), nErrMsg);
			}
			else
			{
				myError = (char*)"out of memory";
			}
		}
	}
}
<|endoftext|>


<commit_before><?hh
abstract class BaseStore {
  protected $class;
  protected $db;

  public function __construct(string $collection = null, string $class = null) {
    if (defined('static::COLLECTION') && defined('static::MODEL')) {
      $collection = static::COLLECTION;
      $class = static::MODEL;
    }

    invariant($collection && $class, 'Collection or class not provided');

    $this->collection = $collection;
    $this->class = $class;
    $this->db = MongoInstance::get($collection);
  }

  public function db() {
    return $this->db;
  }

  public function find(
    array $query = [],
    ?int $skip = 0,
    ?int $limit = 0): BaseModel {
    $docs = $this->db->find($query);
    $class = $this->class;

    if ($skip !== null) {
      $docs = $docs->skip($skip);
    }

    if ($limit !== null) {
      $limit = $docs->limit($limit);
    }

    foreach ($docs as $doc) {
      yield new $class($doc);
    }
  }

  public function distinct(string $key, array $query = []) {
    $docs = $this->db->distinct($key, $query);
    return !is_array($docs) ? [] : $docs;
  }

  public function findOne(array $query): ?BaseModel {
    $doc = $this->db->findOne($query);
    $class = $this->class;
    return $doc ? new $class($doc) : null;
  }

  public function findById(MongoId $id): ?BaseModel {
    return $this->findOne(['_id' => $id]);
  }

  public function count(array $query): int {
    return $this->db->count($query);
  }

  protected function ensureType(BaseModel $item): bool {
    return class_exists($this->class) && is_a($item, $this->class);
  }

  public function remove(BaseModel $item) {
    if (!$this->ensureType($item)) {
      throw new Exception('Invalid object provided, expected ' . $this->class);
      return false;
    }

    if ($item == null) {
      return false;
    }

    try {
      if ($item->getID()) {
        $this->db->remove($item->document());
        return true;
      } else {
        return false;
      }
    } catch (MongoException $e) {
      l('MongoException:', $e->getMessage());
      return false;
    }
  }

  public function removeWhere($query = []) {
    try {
      $this->db->remove($query);
      return true;
    } catch (MongoException $e) {
      l('MongoException:', $e->getMessage());
      return false;
    }
  }

  public function removeById($id) {
    return $this->removeWhere(['_id' => mid($id)]);
  }

  public function aggregate(BaseAggregation $aggregation) {
    return call_user_func_array(
      [$this->db, 'aggregate'],
      $aggregation->getPipeline());
  }

  public function mapReduce(
    MongoCode $map,
    MongoCode $reduce,
    array $query = null,
    array $config = null) {

    $options = [
      'mapreduce' => $this->collection,
      'map' => $map,
      'reduce' => $reduce,
      'out' => ['inline' => true]];

    if ($query) {
      $options['query'] = $query;
    }

    if ($config) {
      unset($options['mapreduce']);
      unset($options['map']);
      unset($options['reduce']);
      unset($options['query']);
      $options = array_merge($options, $config);
    }

    $res = MongoInstance::get()->command($options);

    if (idx($res, 'ok')) {
      return $res;
    } else {
      l('MapReduce error:', $res);
      return null;
    }
  }

  public function save(BaseModel &$item) {
    if (!$this->ensureType($item)) {
      throw new Exception('Invalid object provided, expected ' . $this->class);
      return false;
    }

    if ($item == null) {
      return false;
    }

    try {
      if (!$item->getID()) {
        $id = new MongoId();
        $item->setID($id);
        $document = $item->document();
        $this->db->insert($document);
      } else {
        $document = $item->document();
        $this->db->save($document);
      }
      return true;
    } catch (MongoException $e) {
      l('MongoException:', $e->getMessage());
      return false;
    }
    return true;
  }
}

class BaseStoreCursor {
  protected
    $class,
    $count,
    $cursor,
    $next;

  public function __construct($class, $count, $cursor, $skip, $limit) {
    $this->class = $class;
    $this->count = $count;
    $this->cursor = $cursor;
    $this->next = $count > $limit ? $skip + 1 : null;
  }

  public function count() {
    return $this->count;
  }

  public function nextPage() {
    return $this->next;
  }

  public function docs() {
    $class = $this->class;
    foreach ($this->cursor as $entry) {
      yield new $class($entry);
    }
  }
}

class BaseAggregation {
  protected $pipeline;
  public function __construct() {
    $this->pipeline = [];
  }

  public function getPipeline() {
    return $this->pipeline;
  }

  public function project(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$project' => $spec];
    }
    return $this;
  }

  public function match(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$match' => $spec];
    }
    return $this;
  }

  public function limit($limit) {
    $this->pipeline[] = ['$limit' => $limit];
    return $this;
  }

  public function skip($skip) {
    $this->pipeline[] = ['$skip' => $skip];
    return $this;
  }

  public function unwind($field) {
    $this->pipeline[] = ['$unwind' => '$' . $field];
    return $this;
  }

  public function group(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$group' => $spec];
    }
    return $this;
  }

  public function sort(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$sort' => $spec];
    }
    return $this;
  }

  public static function addToSet(string $field): array {
    return ['$addToSet' => '$' . $field];
  }

  public static function sum($value = 1): array {
    if (!is_numeric($value)) {
      $value = '$' . $value;
    }
    return ['$sum' => $value];
  }

  public function __call($name, $args) {
    $field = array_pop($args);
    switch ($name) {
      case 'addToSet':
      case 'first':
      case 'last':
      case 'max':
      case 'min':
      case 'avg':
        return ['$' . $name => '$' . $field];
    }

    throw new RuntimeException('Method not found: ' . $name);
  }

  public function push($field) {
    if (is_array($field)) {
      foreach ($field as &$f) {
        $f = '$' . $f;
      }
    } else {
      $field = '$' . $field;
    }

    return ['$push' => $field];
  }
}

abstract class BaseModel {
  public ?MongoId $_id;
  public function __construct(array<string, mixed> $document = []) {

    foreach ($document as $key => $value) {
      if (property_exists($this, $key)) {
        $this->$key = $key == '_id' ? mid($value) : $value;
      }
    }
  }

  public function document(): array<string, mixed> {
    return get_object_vars($this);
  }

  final public function getID(): ?MongoId {
    return $this->_id;
  }

  final public function setID(MongoId $_id): void {
    $this->_id = $_id;
  }

  public function __call($method, $args) {
    if (strpos($method, 'get') === 0) {
      $op = 'get';
    } elseif (strpos($method, 'set') === 0) {
      $op = 'set';
    } elseif (strpos($method, 'remove') === 0) {
      $op = 'remove';
    } elseif (strpos($method, 'has') === 0) {
      $op = 'has';
    } else {
      $e = sprintf('Method "%s" not found in %s', $method, get_called_class());
      throw new RuntimeException($e);
      return null;
    }

    // $method = preg_replace('/^(get|set|remove|has)/i', '', $method);
    $method = preg_replace('/^(get|set)/i', '', $method);

    preg_match_all(
      '!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!',
      $method,
      $matches);

    $ret = $matches[0];
    foreach ($ret as &$match) {
      $match = $match == strtoupper($match) ?
        strtolower($match) :
        lcfirst($match);
    }

    $field = implode('_', $ret);

    $arg = array_pop($args);

    switch ($op) {
      case 'set':
        $this->$field = $arg;
        return;

      case 'get':
        invariant(
          property_exists($this, $field),
          '%s is not a valid field for %s',
          $field,
          get_called_class());
        return $this->$field;
    }
  }
}
<commit_msg>BaseStore methods can now be called both as static and as instance method<commit_after><?hh
abstract class BaseStore {
  protected $class;
  protected $db;

  protected static $instance;

  public function __construct(string $collection = null, string $class = null) {
    if (defined('static::COLLECTION') && defined('static::MODEL')) {
      $collection = static::COLLECTION;
      $class = static::MODEL;
    }

    invariant($collection && $class, 'Collection or class not provided');

    $this->collection = $collection;
    $this->class = $class;
    $this->db = MongoInstance::get($collection);
  }

  protected static function i() {
    if (!self::$instance) {
      self::$instance = new static();
    }

    return self::$instance;
  }

  public function db() {
    return self::i()->db;
  }

  public function find(
    array $query = [],
    ?int $skip = 0,
    ?int $limit = 0): BaseModel {
    $docs = self::i()->db->find($query);
    $class = self::i()->class;

    if ($skip !== null) {
      $docs = $docs->skip($skip);
    }

    if ($limit !== null) {
      $limit = $docs->limit($limit);
    }

    foreach ($docs as $doc) {
      yield new $class($doc);
    }
  }

  public function distinct(string $key, array $query = []) {
    $docs = self::i()->db->distinct($key, $query);
    return !is_array($docs) ? [] : $docs;
  }

  public function findOne(array $query): ?BaseModel {
    $doc = self::i()->db->findOne($query);
    $class = self::i()->class;
    return $doc ? new $class($doc) : null;
  }

  public function findById(MongoId $id): ?BaseModel {
    return self::i()->findOne(['_id' => $id]);
  }

  public function count(array $query): int {
    return self::i()->db->count($query);
  }

  protected function ensureType(BaseModel $item): bool {
    return class_exists(self::i()->class) && is_a($item, $this->class);
  }

  public function remove(BaseModel $item) {
    if (!self::i()->ensureType($item)) {
      throw new Exception(
        'Invalid object provided, expected ' . self::i()->class);
      return false;
    }

    if ($item == null) {
      return false;
    }

    try {
      if ($item->getID()) {
        self::i()->db->remove($item->document());
        return true;
      } else {
        return false;
      }
    } catch (MongoException $e) {
      l('MongoException:', $e->getMessage());
      return false;
    }
  }

  public function removeWhere($query = []) {
    try {
      self::i()->db->remove($query);
      return true;
    } catch (MongoException $e) {
      l('MongoException:', $e->getMessage());
      return false;
    }
  }

  public function removeById($id) {
    return self::i()->removeWhere(['_id' => mid($id)]);
  }

  public function aggregate(BaseAggregation $aggregation) {
    return call_user_func_array(
      [self::i()->db, 'aggregate'],
      $aggregation->getPipeline());
  }

  public function mapReduce(
    MongoCode $map,
    MongoCode $reduce,
    array $query = null,
    array $config = null) {

    $options = [
      'mapreduce' => self::i()->collection,
      'map' => $map,
      'reduce' => $reduce,
      'out' => ['inline' => true]];

    if ($query) {
      $options['query'] = $query;
    }

    if ($config) {
      unset($options['mapreduce']);
      unset($options['map']);
      unset($options['reduce']);
      unset($options['query']);
      $options = array_merge($options, $config);
    }

    $res = MongoInstance::get()->command($options);

    if (idx($res, 'ok')) {
      return $res;
    } else {
      l('MapReduce error:', $res);
      return null;
    }
  }

  public function save(BaseModel &$item) {
    if (!self::i()->ensureType($item)) {
      throw new Exception(
        'Invalid object provided, expected ' . self::i()->class);
      return false;
    }

    if ($item == null) {
      return false;
    }

    try {
      if (!$item->getID()) {
        $id = new MongoId();
        $item->setID($id);
        $document = $item->document();
        self::i()->db->insert($document);
      } else {
        $document = $item->document();
        self::i()->db->save($document);
      }
      return true;
    } catch (MongoException $e) {
      l('MongoException:', $e->getMessage());
      return false;
    }
    return true;
  }
}

class BaseStoreCursor {
  protected
    $class,
    $count,
    $cursor,
    $next;

  public function __construct($class, $count, $cursor, $skip, $limit) {
    $this->class = $class;
    $this->count = $count;
    $this->cursor = $cursor;
    $this->next = $count > $limit ? $skip + 1 : null;
  }

  public function count() {
    return $this->count;
  }

  public function nextPage() {
    return $this->next;
  }

  public function docs() {
    $class = $this->class;
    foreach ($this->cursor as $entry) {
      yield new $class($entry);
    }
  }
}

class BaseAggregation {
  protected $pipeline;
  public function __construct() {
    $this->pipeline = [];
  }

  public function getPipeline() {
    return $this->pipeline;
  }

  public function project(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$project' => $spec];
    }
    return $this;
  }

  public function match(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$match' => $spec];
    }
    return $this;
  }

  public function limit($limit) {
    $this->pipeline[] = ['$limit' => $limit];
    return $this;
  }

  public function skip($skip) {
    $this->pipeline[] = ['$skip' => $skip];
    return $this;
  }

  public function unwind($field) {
    $this->pipeline[] = ['$unwind' => '$' . $field];
    return $this;
  }

  public function group(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$group' => $spec];
    }
    return $this;
  }

  public function sort(array $spec) {
    if (!empty($spec)) {
      $this->pipeline[] = ['$sort' => $spec];
    }
    return $this;
  }

  public static function addToSet(string $field): array {
    return ['$addToSet' => '$' . $field];
  }

  public static function sum($value = 1): array {
    if (!is_numeric($value)) {
      $value = '$' . $value;
    }
    return ['$sum' => $value];
  }

  public function __call($name, $args) {
    $field = array_pop($args);
    switch ($name) {
      case 'addToSet':
      case 'first':
      case 'last':
      case 'max':
      case 'min':
      case 'avg':
        return ['$' . $name => '$' . $field];
    }

    throw new RuntimeException('Method not found: ' . $name);
  }

  public function push($field) {
    if (is_array($field)) {
      foreach ($field as &$f) {
        $f = '$' . $f;
      }
    } else {
      $field = '$' . $field;
    }

    return ['$push' => $field];
  }
}

abstract class BaseModel {
  public ?MongoId $_id;
  public function __construct(array<string, mixed> $document = []) {

    foreach ($document as $key => $value) {
      if (property_exists($this, $key)) {
        $this->$key = $key == '_id' ? mid($value) : $value;
      }
    }
  }

  public function document(): array<string, mixed> {
    return get_object_vars($this);
  }

  final public function getID(): ?MongoId {
    return $this->_id;
  }

  final public function setID(MongoId $_id): void {
    $this->_id = $_id;
  }

  public function __call($method, $args) {
    if (strpos($method, 'get') === 0) {
      $op = 'get';
    } elseif (strpos($method, 'set') === 0) {
      $op = 'set';
    } elseif (strpos($method, 'remove') === 0) {
      $op = 'remove';
    } elseif (strpos($method, 'has') === 0) {
      $op = 'has';
    } else {
      $e = sprintf('Method "%s" not found in %s', $method, get_called_class());
      throw new RuntimeException($e);
      return null;
    }

    // $method = preg_replace('/^(get|set|remove|has)/i', '', $method);
    $method = preg_replace('/^(get|set)/i', '', $method);

    preg_match_all(
      '!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!',
      $method,
      $matches);

    $ret = $matches[0];
    foreach ($ret as &$match) {
      $match = $match == strtoupper($match) ?
        strtolower($match) :
        lcfirst($match);
    }

    $field = implode('_', $ret);

    $arg = array_pop($args);

    switch ($op) {
      case 'set':
        $this->$field = $arg;
        return;

      case 'get':
        invariant(
          property_exists($this, $field),
          '%s is not a valid field for %s',
          $field,
          get_called_class());
        return $this->$field;
    }
  }
}
<|endoftext|>


<commit_before>//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include <memory>
#include <mutex>
#include <set>
#include <unordered_map>

using llvm::object::Archive;
using llvm::object::ObjectFile;
using llvm::object::SymbolRef;
using llvm::object::symbol_iterator;
using llvm::object::object_error;

namespace lld {

namespace {

/// \brief The FileArchive class represents an Archive Library file
class FileArchive : public lld::ArchiveLibraryFile {
public:
  FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
              StringRef path, bool logLoading)
      : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
        _registry(reg), _logLoading(logLoading) {}

  /// \brief Check if any member of the archive contains an Atom with the
  /// specified name and return the File object for that member, or nullptr.
  const File *find(StringRef name, bool dataSymbolOnly) const override {
    auto member = _symbolMemberMap.find(name);
    if (member == _symbolMemberMap.end())
      return nullptr;
    Archive::child_iterator ci = member->second;

    // Don't return a member already returned
    const char *memberStart = ci->getBuffer().data();
    if (_membersInstantiated.count(memberStart))
      return nullptr;
    if (dataSymbolOnly && !isDataSymbol(ci, name))
      return nullptr;

    _membersInstantiated.insert(memberStart);

    // Check if a file is preloaded.
    {
      std::lock_guard<std::mutex> lock(_mutex);
      auto it = _preloaded.find(memberStart);
      if (it != _preloaded.end()) {
        std::unique_ptr<Future<const File *>> &p = it->second;
        Future<const File *> *future = p.get();
        return future->get();
      }
    }

    std::unique_ptr<File> result;
    if (instantiateMember(ci, result))
      return nullptr;

    // give up the pointer so that this object no longer manages it
    return result.release();
  }

  // Instantiate a member file containing a given symbol name.
  void preload(TaskGroup &group, StringRef name) override {
    auto member = _symbolMemberMap.find(name);
    if (member == _symbolMemberMap.end())
      return;
    Archive::child_iterator ci = member->second;

    // Do nothing if a member is already instantiated.
    const char *memberStart = ci->getBuffer().data();
    if (_membersInstantiated.count(memberStart))
      return;

    std::lock_guard<std::mutex> lock(_mutex);
    if (_preloaded.find(memberStart) != _preloaded.end())
      return;

    // Instantiate the member
    auto *future = new Future<const File *>();
    _preloaded[memberStart] = std::unique_ptr<Future<const File *>>(future);

    group.spawn([=] {
      std::unique_ptr<File> result;
      std::error_code ec = instantiateMember(ci, result);
      future->set(ec ? nullptr : result.release());
    });
  }

  /// \brief parse each member
  std::error_code
  parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
    if (std::error_code ec = parse())
      return ec;
    for (auto mf = _archive->child_begin(), me = _archive->child_end();
         mf != me; ++mf) {
      std::unique_ptr<File> file;
      if (std::error_code ec = instantiateMember(mf, file))
        return ec;
      result.push_back(std::move(file));
    }
    return std::error_code();
  }

  const atom_collection<DefinedAtom> &defined() const override {
    return _definedAtoms;
  }

  const atom_collection<UndefinedAtom> &undefined() const override {
    return _undefinedAtoms;
  }

  const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
    return _sharedLibraryAtoms;
  }

  const atom_collection<AbsoluteAtom> &absolute() const override {
    return _absoluteAtoms;
  }

  std::error_code buildTableOfContents() {
    DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
                                       << "Table of contents for archive '"
                                       << _archive->getFileName() << "':\n");
    for (auto i = _archive->symbol_begin(), e = _archive->symbol_end();
         i != e; ++i) {
      StringRef name = i->getName();
      ErrorOr<Archive::child_iterator> memberOrErr = i->getMember();
      if (std::error_code ec = memberOrErr.getError())
        return ec;
      Archive::child_iterator member = memberOrErr.get();
      DEBUG_WITH_TYPE(
          "FileArchive",
          llvm::dbgs() << llvm::format("0x%08llX ", member->getBuffer().data())
                       << "'" << name << "'\n");
      _symbolMemberMap[name] = member;
    }
    return std::error_code();
  }

  /// Returns a set of all defined symbols in the archive.
  std::set<StringRef> getDefinedSymbols() override {
    parse();
    std::set<StringRef> ret;
    for (const auto &e : _symbolMemberMap)
      ret.insert(e.first);
    return ret;
  }

protected:
  std::error_code doParse() override {
    // Make Archive object which will be owned by FileArchive object.
    std::error_code ec;
    _archive.reset(new Archive(_mb->getMemBufferRef(), ec));
    if (ec)
      return ec;
    if ((ec = buildTableOfContents()))
      return ec;
    return std::error_code();
  }

private:
  std::error_code
  instantiateMember(Archive::child_iterator member,
                    std::unique_ptr<File> &result) const {
    ErrorOr<llvm::MemoryBufferRef> mbOrErr = member->getMemoryBufferRef();
    if (std::error_code ec = mbOrErr.getError())
      return ec;
    llvm::MemoryBufferRef mb = mbOrErr.get();
    std::string memberPath = (_archive->getFileName() + "("
                           + mb.getBufferIdentifier() + ")").str();

    if (_logLoading)
      llvm::errs() << memberPath << "\n";

    std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
        mb.getBuffer(), mb.getBufferIdentifier(), false));

    std::vector<std::unique_ptr<File>> files;
    if (std::error_code ec = _registry.loadFile(std::move(memberMB), files))
      return ec;
    assert(files.size() == 1);
    result = std::move(files[0]);
    if (std::error_code ec = result->parse())
      return ec;
    result->setArchivePath(_archive->getFileName());

    // The memory buffer is co-owned by the archive file and the children,
    // so that the bufffer is deallocated when all the members are destructed.
    result->setSharedMemoryBuffer(_mb);
    return std::error_code();
  }

  // Parses the given memory buffer as an object file, and returns true
  // code if the given symbol is a data symbol. If the symbol is not a data
  // symbol or does not exist, returns false.
  bool isDataSymbol(Archive::child_iterator member, StringRef symbol) const {
    ErrorOr<llvm::MemoryBufferRef> buf = member->getMemoryBufferRef();
    if (buf.getError())
      return false;
    std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(
        buf.get().getBuffer(), buf.get().getBufferIdentifier(), false));

    auto objOrErr(ObjectFile::createObjectFile(mb->getMemBufferRef()));
    if (objOrErr.getError())
      return false;
    std::unique_ptr<ObjectFile> obj = std::move(objOrErr.get());
    SymbolRef::Type symtype;
    uint32_t symflags;
    symbol_iterator ibegin = obj->symbol_begin();
    symbol_iterator iend = obj->symbol_end();
    StringRef symbolname;

    for (symbol_iterator i = ibegin; i != iend; ++i) {
      // Get symbol name
      if (i->getName(symbolname))
        return false;
      if (symbolname != symbol)
        continue;

      // Get symbol flags
      symflags = i->getFlags();

      if (symflags <= SymbolRef::SF_Undefined)
        continue;

      // Get Symbol Type
      if (i->getType(symtype))
        return false;

      if (symtype == SymbolRef::ST_Data)
        return true;
    }
    return false;
  }

private:
  typedef std::unordered_map<StringRef, Archive::child_iterator> MemberMap;
  typedef std::set<const char *> InstantiatedSet;

  std::shared_ptr<MemoryBuffer> _mb;
  const Registry &_registry;
  std::unique_ptr<Archive> _archive;
  mutable MemberMap _symbolMemberMap;
  mutable InstantiatedSet _membersInstantiated;
  atom_collection_vector<DefinedAtom> _definedAtoms;
  atom_collection_vector<UndefinedAtom> _undefinedAtoms;
  atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
  atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
  bool _logLoading;
  mutable std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
  mutable std::map<const char *, std::unique_ptr<Future<const File *>>> _preloaded;
  mutable std::mutex _mutex;
};

class ArchiveReader : public Reader {
public:
  ArchiveReader(bool logLoading) : _logLoading(logLoading) {}

  bool canParse(file_magic magic, StringRef,
                const MemoryBuffer &) const override {
    return (magic == llvm::sys::fs::file_magic::archive);
  }

  std::error_code
  loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
           std::vector<std::unique_ptr<File>> &result) const override {
    StringRef path = mb->getBufferIdentifier();
    std::unique_ptr<FileArchive> file(
        new FileArchive(std::move(mb), reg, path, _logLoading));
    result.push_back(std::move(file));
    return std::error_code();
  }

private:
  bool _logLoading;
};

} // anonymous namespace

void Registry::addSupportArchives(bool logLoading) {
  add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));
}

} // end namespace lld
<commit_msg>Make a private function private.<commit_after>//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include <memory>
#include <mutex>
#include <set>
#include <unordered_map>

using llvm::object::Archive;
using llvm::object::ObjectFile;
using llvm::object::SymbolRef;
using llvm::object::symbol_iterator;
using llvm::object::object_error;

namespace lld {

namespace {

/// \brief The FileArchive class represents an Archive Library file
class FileArchive : public lld::ArchiveLibraryFile {
public:
  FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
              StringRef path, bool logLoading)
      : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())),
        _registry(reg), _logLoading(logLoading) {}

  /// \brief Check if any member of the archive contains an Atom with the
  /// specified name and return the File object for that member, or nullptr.
  const File *find(StringRef name, bool dataSymbolOnly) const override {
    auto member = _symbolMemberMap.find(name);
    if (member == _symbolMemberMap.end())
      return nullptr;
    Archive::child_iterator ci = member->second;

    // Don't return a member already returned
    const char *memberStart = ci->getBuffer().data();
    if (_membersInstantiated.count(memberStart))
      return nullptr;
    if (dataSymbolOnly && !isDataSymbol(ci, name))
      return nullptr;

    _membersInstantiated.insert(memberStart);

    // Check if a file is preloaded.
    {
      std::lock_guard<std::mutex> lock(_mutex);
      auto it = _preloaded.find(memberStart);
      if (it != _preloaded.end()) {
        std::unique_ptr<Future<const File *>> &p = it->second;
        Future<const File *> *future = p.get();
        return future->get();
      }
    }

    std::unique_ptr<File> result;
    if (instantiateMember(ci, result))
      return nullptr;

    // give up the pointer so that this object no longer manages it
    return result.release();
  }

  // Instantiate a member file containing a given symbol name.
  void preload(TaskGroup &group, StringRef name) override {
    auto member = _symbolMemberMap.find(name);
    if (member == _symbolMemberMap.end())
      return;
    Archive::child_iterator ci = member->second;

    // Do nothing if a member is already instantiated.
    const char *memberStart = ci->getBuffer().data();
    if (_membersInstantiated.count(memberStart))
      return;

    std::lock_guard<std::mutex> lock(_mutex);
    if (_preloaded.find(memberStart) != _preloaded.end())
      return;

    // Instantiate the member
    auto *future = new Future<const File *>();
    _preloaded[memberStart] = std::unique_ptr<Future<const File *>>(future);

    group.spawn([=] {
      std::unique_ptr<File> result;
      std::error_code ec = instantiateMember(ci, result);
      future->set(ec ? nullptr : result.release());
    });
  }

  /// \brief parse each member
  std::error_code
  parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
    if (std::error_code ec = parse())
      return ec;
    for (auto mf = _archive->child_begin(), me = _archive->child_end();
         mf != me; ++mf) {
      std::unique_ptr<File> file;
      if (std::error_code ec = instantiateMember(mf, file))
        return ec;
      result.push_back(std::move(file));
    }
    return std::error_code();
  }

  const atom_collection<DefinedAtom> &defined() const override {
    return _definedAtoms;
  }

  const atom_collection<UndefinedAtom> &undefined() const override {
    return _undefinedAtoms;
  }

  const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
    return _sharedLibraryAtoms;
  }

  const atom_collection<AbsoluteAtom> &absolute() const override {
    return _absoluteAtoms;
  }

  /// Returns a set of all defined symbols in the archive.
  std::set<StringRef> getDefinedSymbols() override {
    parse();
    std::set<StringRef> ret;
    for (const auto &e : _symbolMemberMap)
      ret.insert(e.first);
    return ret;
  }

protected:
  std::error_code doParse() override {
    // Make Archive object which will be owned by FileArchive object.
    std::error_code ec;
    _archive.reset(new Archive(_mb->getMemBufferRef(), ec));
    if (ec)
      return ec;
    if ((ec = buildTableOfContents()))
      return ec;
    return std::error_code();
  }

private:
  std::error_code
  instantiateMember(Archive::child_iterator member,
                    std::unique_ptr<File> &result) const {
    ErrorOr<llvm::MemoryBufferRef> mbOrErr = member->getMemoryBufferRef();
    if (std::error_code ec = mbOrErr.getError())
      return ec;
    llvm::MemoryBufferRef mb = mbOrErr.get();
    std::string memberPath = (_archive->getFileName() + "("
                           + mb.getBufferIdentifier() + ")").str();

    if (_logLoading)
      llvm::errs() << memberPath << "\n";

    std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer(
        mb.getBuffer(), mb.getBufferIdentifier(), false));

    std::vector<std::unique_ptr<File>> files;
    if (std::error_code ec = _registry.loadFile(std::move(memberMB), files))
      return ec;
    assert(files.size() == 1);
    result = std::move(files[0]);
    if (std::error_code ec = result->parse())
      return ec;
    result->setArchivePath(_archive->getFileName());

    // The memory buffer is co-owned by the archive file and the children,
    // so that the bufffer is deallocated when all the members are destructed.
    result->setSharedMemoryBuffer(_mb);
    return std::error_code();
  }

  // Parses the given memory buffer as an object file, and returns true
  // code if the given symbol is a data symbol. If the symbol is not a data
  // symbol or does not exist, returns false.
  bool isDataSymbol(Archive::child_iterator member, StringRef symbol) const {
    ErrorOr<llvm::MemoryBufferRef> buf = member->getMemoryBufferRef();
    if (buf.getError())
      return false;
    std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(
        buf.get().getBuffer(), buf.get().getBufferIdentifier(), false));

    auto objOrErr(ObjectFile::createObjectFile(mb->getMemBufferRef()));
    if (objOrErr.getError())
      return false;
    std::unique_ptr<ObjectFile> obj = std::move(objOrErr.get());
    SymbolRef::Type symtype;
    uint32_t symflags;
    symbol_iterator ibegin = obj->symbol_begin();
    symbol_iterator iend = obj->symbol_end();
    StringRef symbolname;

    for (symbol_iterator i = ibegin; i != iend; ++i) {
      // Get symbol name
      if (i->getName(symbolname))
        return false;
      if (symbolname != symbol)
        continue;

      // Get symbol flags
      symflags = i->getFlags();

      if (symflags <= SymbolRef::SF_Undefined)
        continue;

      // Get Symbol Type
      if (i->getType(symtype))
        return false;

      if (symtype == SymbolRef::ST_Data)
        return true;
    }
    return false;
  }

  std::error_code buildTableOfContents() {
    DEBUG_WITH_TYPE("FileArchive", llvm::dbgs()
                                       << "Table of contents for archive '"
                                       << _archive->getFileName() << "':\n");
    for (auto i = _archive->symbol_begin(), e = _archive->symbol_end();
         i != e; ++i) {
      StringRef name = i->getName();
      ErrorOr<Archive::child_iterator> memberOrErr = i->getMember();
      if (std::error_code ec = memberOrErr.getError())
        return ec;
      Archive::child_iterator member = memberOrErr.get();
      DEBUG_WITH_TYPE(
          "FileArchive",
          llvm::dbgs() << llvm::format("0x%08llX ", member->getBuffer().data())
                       << "'" << name << "'\n");
      _symbolMemberMap[name] = member;
    }
    return std::error_code();
  }

  typedef std::unordered_map<StringRef, Archive::child_iterator> MemberMap;
  typedef std::set<const char *> InstantiatedSet;

  std::shared_ptr<MemoryBuffer> _mb;
  const Registry &_registry;
  std::unique_ptr<Archive> _archive;
  mutable MemberMap _symbolMemberMap;
  mutable InstantiatedSet _membersInstantiated;
  atom_collection_vector<DefinedAtom> _definedAtoms;
  atom_collection_vector<UndefinedAtom> _undefinedAtoms;
  atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
  atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
  bool _logLoading;
  mutable std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers;
  mutable std::map<const char *, std::unique_ptr<Future<const File *>>> _preloaded;
  mutable std::mutex _mutex;
};

class ArchiveReader : public Reader {
public:
  ArchiveReader(bool logLoading) : _logLoading(logLoading) {}

  bool canParse(file_magic magic, StringRef,
                const MemoryBuffer &) const override {
    return (magic == llvm::sys::fs::file_magic::archive);
  }

  std::error_code
  loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry &reg,
           std::vector<std::unique_ptr<File>> &result) const override {
    StringRef path = mb->getBufferIdentifier();
    std::unique_ptr<FileArchive> file(
        new FileArchive(std::move(mb), reg, path, _logLoading));
    result.push_back(std::move(file));
    return std::error_code();
  }

private:
  bool _logLoading;
};

} // anonymous namespace

void Registry::addSupportArchives(bool logLoading) {
  add(std::unique_ptr<Reader>(new ArchiveReader(logLoading)));
}

} // end namespace lld
<|endoftext|>


<commit_before>//
//  IORemoteCommand.cpp
//  libio
//
//  Created by Sidney Just
//  Copyright (c) 2012 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#include "IORemoteCommand.h"

#ifdef super
#undef super
#endif
#define super IOEventSource

IORegisterClass(IORemoteCommand, super);

IORemoteCommand *IORemoteCommand::init()
{
	return (IORemoteCommand *)super::init();
}

IORemoteCommand *IORemoteCommand::initWithAction(IOObject *owner, Action action)
{
	return (IORemoteCommand *)super::initWithAction(owner, action);
}

void IORemoteCommand::doWork()
{
	kern_spinlock_lock(&_lock);

	if(!_waiting || _cancelled)
	{
		kern_spinlock_unlock(&_lock);
		return;
	}

	_executing = true;
	kern_spinlock_unlock(&_lock);

	_action(_owner, _arg0, _arg1, _arg2, _arg3, _arg4);

	kern_spinlock_lock(&_lock);
	_executing = false;
	_executed = true;
	_waiting = false;
	kern_spinlock_unlock(&_lock);

	_caller->wakeup();
}

IOReturn IORemoteCommand::executeAction(Action action, void *arg0, void *arg1, void *arg2, void *arg3, void *arg4)
{
	Action temp = _action;
	_action = action;

	IOReturn result = executeCommand(arg0, arg1, arg2, arg3, arg4);

	_action = temp;
	return result;
}

IOReturn IORemoteCommand::executeCommand(void *arg0, void *arg1, void *arg2, void *arg3, void *arg4)
{
	if(_runLoop->isOnThread())
	{
		_action(_owner, _arg0, _arg1, _arg2, _arg3, _arg4);
		return kIOReturnSuccess;
	}

	kern_spinlock_lock(&_lock);

	_cancelled = false;
	_waiting = true;
	_caller = IOThread::currentThread();

	_arg0 = arg0;
	_arg1 = arg1;
	_arg2 = arg2;
	_arg3 = arg3;
	_arg4 = arg4;

	kern_spinlock_unlock(&_lock);

	uint64_t timeout = (_timeout > 0) ? _timeout : 10;
	while(1)
	{
		_caller->sleep(timeout);

		kern_spinlock_lock(&_lock);

		if(_executed)
		{
			kern_spinlock_unlock(&_lock);
			return kIOReturnSuccess;
		}

		if(_timeout > 0 && !_executing)
		{
			_cancelled = true;
			kern_spinlock_unlock(&_lock);
			return kIOReturnTimeout;
		}

		kern_spinlock_unlock(&_lock);
	}
}
<commit_msg>Fixed a bug inside IORemoteCommand (should be merged with Firedrake!)<commit_after>//
//  IORemoteCommand.cpp
//  libio
//
//  Created by Sidney Just
//  Copyright (c) 2012 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#include "IORemoteCommand.h"

#ifdef super
#undef super
#endif
#define super IOEventSource

IORegisterClass(IORemoteCommand, super);

IORemoteCommand *IORemoteCommand::init()
{
	return (IORemoteCommand *)super::init();
}

IORemoteCommand *IORemoteCommand::initWithAction(IOObject *owner, Action action)
{
	return (IORemoteCommand *)super::initWithAction(owner, action);
}

void IORemoteCommand::doWork()
{
	kern_spinlock_lock(&_lock);

	if(!_waiting || _cancelled)
	{
		kern_spinlock_unlock(&_lock);
		return;
	}

	_executing = true;
	kern_spinlock_unlock(&_lock);

	_action(_owner, _arg0, _arg1, _arg2, _arg3, _arg4);

	kern_spinlock_lock(&_lock);
	_executing = false;
	_executed = true;
	_waiting = false;
	kern_spinlock_unlock(&_lock);

	_caller->wakeup();
}

IOReturn IORemoteCommand::executeAction(Action action, void *arg0, void *arg1, void *arg2, void *arg3, void *arg4)
{
	Action temp = _action;
	_action = action;

	IOReturn result = executeCommand(arg0, arg1, arg2, arg3, arg4);

	_action = temp;
	return result;
}

IOReturn IORemoteCommand::executeCommand(void *arg0, void *arg1, void *arg2, void *arg3, void *arg4)
{
	if(_runLoop->isOnThread())
	{
		_action(_owner, arg0, arg1, arg2, arg3, arg4);
		return kIOReturnSuccess;
	}

	kern_spinlock_lock(&_lock);

	_cancelled = false;
	_waiting = true;
	_caller = IOThread::currentThread();

	_arg0 = arg0;
	_arg1 = arg1;
	_arg2 = arg2;
	_arg3 = arg3;
	_arg4 = arg4;

	kern_spinlock_unlock(&_lock);

	uint64_t timeout = (_timeout > 0) ? _timeout : 10;
	while(1)
	{
		_caller->sleep(timeout);

		kern_spinlock_lock(&_lock);

		if(_executed)
		{
			kern_spinlock_unlock(&_lock);
			return kIOReturnSuccess;
		}

		if(_timeout > 0 && !_executing)
		{
			_cancelled = true;
			kern_spinlock_unlock(&_lock);
			return kIOReturnTimeout;
		}

		kern_spinlock_unlock(&_lock);
	}
}
<|endoftext|>