<commit_before>//===-- FunctionLoweringInfo.cpp ------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This implements routines for translating functions from LLVM IR into
// Machine IR.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "function-lowering-info"
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetFrameInfo.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetLowering.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include <algorithm>
using namespace llvm;

/// isUsedOutsideOfDefiningBlock - Return true if this instruction is used by
/// PHI nodes or outside of the basic block that defines it, or used by a
/// switch or atomic instruction, which may expand to multiple basic blocks.
static bool isUsedOutsideOfDefiningBlock(const Instruction *I) {
  if (I->use_empty()) return false;
  if (isa<PHINode>(I)) return true;
  const BasicBlock *BB = I->getParent();
  for (Value::const_use_iterator UI = I->use_begin(), E = I->use_end();
        UI != E; ++UI)
    if (cast<Instruction>(*UI)->getParent() != BB || isa<PHINode>(*UI))
      return true;
  return false;
}

/// isOnlyUsedInEntryBlock - If the specified argument is only used in the
/// entry block, return true.  This includes arguments used by switches, since
/// the switch may expand into multiple basic blocks.
static bool isOnlyUsedInEntryBlock(const Argument *A, bool EnableFastISel) {
  // With FastISel active, we may be splitting blocks, so force creation
  // of virtual registers for all non-dead arguments.
  if (EnableFastISel)
    return A->use_empty();

  const BasicBlock *Entry = A->getParent()->begin();
  for (Value::const_use_iterator UI = A->use_begin(), E = A->use_end();
       UI != E; ++UI)
    if (cast<Instruction>(*UI)->getParent() != Entry || isa<SwitchInst>(*UI))
      return false;  // Use not in entry block.
  return true;
}

FunctionLoweringInfo::FunctionLoweringInfo(const TargetLowering &tli)
  : TLI(tli) {
}

void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf) {
  Fn = &fn;
  MF = &mf;
  RegInfo = &MF->getRegInfo();

  // Check whether the function can return without sret-demotion.
  SmallVector<ISD::OutputArg, 4> Outs;
  GetReturnInfo(Fn->getReturnType(),
                Fn->getAttributes().getRetAttributes(), Outs, TLI);
  CanLowerReturn = TLI.CanLowerReturn(Fn->getCallingConv(), Fn->isVarArg(),
                                      Outs, Fn->getContext());

  // Create a vreg for each argument register that is not dead and is used
  // outside of the entry block for the function.
  for (Function::const_arg_iterator AI = Fn->arg_begin(), E = Fn->arg_end();
       AI != E; ++AI)
    if (!isOnlyUsedInEntryBlock(AI, EnableFastISel))
      InitializeRegForValue(AI);

  // Initialize the mapping of values to registers.  This is only set up for
  // instruction values that are used outside of the block that defines
  // them.
  Function::const_iterator BB = Fn->begin(), EB = Fn->end();
  for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); I != E; ++I)
    if (const AllocaInst *AI = dyn_cast<AllocaInst>(I))
      if (const ConstantInt *CUI = dyn_cast<ConstantInt>(AI->getArraySize())) {
        const Type *Ty = AI->getAllocatedType();
        uint64_t TySize = TLI.getTargetData()->getTypeAllocSize(Ty);
        unsigned Align =
          std::max((unsigned)TLI.getTargetData()->getPrefTypeAlignment(Ty),
                   AI->getAlignment());

        TySize *= CUI->getZExtValue();   // Get total allocated size.
        if (TySize == 0) TySize = 1; // Don't create zero-sized stack objects.
        StaticAllocaMap[AI] =
          MF->getFrameInfo()->CreateStackObject(TySize, Align, false);
      }

  for (; BB != EB; ++BB)
    for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); I != E; ++I)
      if (isUsedOutsideOfDefiningBlock(I))
        if (!isa<AllocaInst>(I) ||
            !StaticAllocaMap.count(cast<AllocaInst>(I)))
          InitializeRegForValue(I);

  // Create an initial MachineBasicBlock for each LLVM BasicBlock in F.  This
  // also creates the initial PHI MachineInstrs, though none of the input
  // operands are populated.
  for (BB = Fn->begin(); BB != EB; ++BB) {
    MachineBasicBlock *MBB = mf.CreateMachineBasicBlock(BB);
    MBBMap[BB] = MBB;
    MF->push_back(MBB);

    // Transfer the address-taken flag. This is necessary because there could
    // be multiple MachineBasicBlocks corresponding to one BasicBlock, and only
    // the first one should be marked.
    if (BB->hasAddressTaken())
      MBB->setHasAddressTaken();

    // Create Machine PHI nodes for LLVM PHI nodes, lowering them as
    // appropriate.
    for (BasicBlock::const_iterator I = BB->begin();
         const PHINode *PN = dyn_cast<PHINode>(I); ++I) {
      if (PN->use_empty()) continue;

      DebugLoc DL = PN->getDebugLoc();
      unsigned PHIReg = ValueMap[PN];
      assert(PHIReg && "PHI node does not have an assigned virtual register!");

      SmallVector<EVT, 4> ValueVTs;
      ComputeValueVTs(TLI, PN->getType(), ValueVTs);
      for (unsigned vti = 0, vte = ValueVTs.size(); vti != vte; ++vti) {
        EVT VT = ValueVTs[vti];
        unsigned NumRegisters = TLI.getNumRegisters(Fn->getContext(), VT);
        const TargetInstrInfo *TII = MF->getTarget().getInstrInfo();
        for (unsigned i = 0; i != NumRegisters; ++i)
          BuildMI(MBB, DL, TII->get(TargetOpcode::PHI), PHIReg + i);
        PHIReg += NumRegisters;
      }
    }
  }

  // Mark landing pad blocks.
  for (BB = Fn->begin(); BB != EB; ++BB)
    if (const InvokeInst *Invoke = dyn_cast<InvokeInst>(BB->getTerminator()))
      MBBMap[Invoke->getSuccessor(1)]->setIsLandingPad();
}

/// clear - Clear out all the function-specific state. This returns this
/// FunctionLoweringInfo to an empty state, ready to be used for a
/// different function.
void FunctionLoweringInfo::clear() {
  assert(CatchInfoFound.size() == CatchInfoLost.size() &&
         "Not all catch info was assigned to a landing pad!");

  MBBMap.clear();
  ValueMap.clear();
  StaticAllocaMap.clear();
#ifndef NDEBUG
  CatchInfoLost.clear();
  CatchInfoFound.clear();
#endif
  LiveOutRegInfo.clear();
  ArgDbgValues.clear();
  RegFixups.clear();
}

/// CreateReg - Allocate a single virtual register for the given type.
unsigned FunctionLoweringInfo::CreateReg(EVT VT) {
  return RegInfo->createVirtualRegister(TLI.getRegClassFor(VT));
}

/// CreateRegs - Allocate the appropriate number of virtual registers of
/// the correctly promoted or expanded types.  Assign these registers
/// consecutive vreg numbers and return the first assigned number.
///
/// In the case that the given value has struct or array type, this function
/// will assign registers for each member or element.
///
unsigned FunctionLoweringInfo::CreateRegs(const Type *Ty) {
  SmallVector<EVT, 4> ValueVTs;
  ComputeValueVTs(TLI, Ty, ValueVTs);

  unsigned FirstReg = 0;
  for (unsigned Value = 0, e = ValueVTs.size(); Value != e; ++Value) {
    EVT ValueVT = ValueVTs[Value];
    EVT RegisterVT = TLI.getRegisterType(Ty->getContext(), ValueVT);

    unsigned NumRegs = TLI.getNumRegisters(Ty->getContext(), ValueVT);
    for (unsigned i = 0; i != NumRegs; ++i) {
      unsigned R = CreateReg(RegisterVT);
      if (!FirstReg) FirstReg = R;
    }
  }
  return FirstReg;
}

/// AddCatchInfo - Extract the personality and type infos from an eh.selector
/// call, and add them to the specified machine basic block.
void llvm::AddCatchInfo(const CallInst &I, MachineModuleInfo *MMI,
                        MachineBasicBlock *MBB) {
  // Inform the MachineModuleInfo of the personality for this landing pad.
  const ConstantExpr *CE = cast<ConstantExpr>(I.getArgOperand(1));
  assert(CE->getOpcode() == Instruction::BitCast &&
         isa<Function>(CE->getOperand(0)) &&
         "Personality should be a function");
  MMI->addPersonality(MBB, cast<Function>(CE->getOperand(0)));

  // Gather all the type infos for this landing pad and pass them along to
  // MachineModuleInfo.
  std::vector<const GlobalVariable *> TyInfo;
  unsigned N = I.getNumArgOperands();

  for (unsigned i = N - 1; i > 1; --i) {
    if (const ConstantInt *CI = dyn_cast<ConstantInt>(I.getArgOperand(i))) {
      unsigned FilterLength = CI->getZExtValue();
      unsigned FirstCatch = i + FilterLength + !FilterLength;
      assert(FirstCatch <= N && "Invalid filter length");

      if (FirstCatch < N) {
        TyInfo.reserve(N - FirstCatch);
        for (unsigned j = FirstCatch; j < N; ++j)
          TyInfo.push_back(ExtractTypeInfo(I.getArgOperand(j)));
        MMI->addCatchTypeInfo(MBB, TyInfo);
        TyInfo.clear();
      }

      if (!FilterLength) {
        // Cleanup.
        MMI->addCleanup(MBB);
      } else {
        // Filter.
        TyInfo.reserve(FilterLength - 1);
        for (unsigned j = i + 1; j < FirstCatch; ++j)
          TyInfo.push_back(ExtractTypeInfo(I.getArgOperand(j)));
        MMI->addFilterTypeInfo(MBB, TyInfo);
        TyInfo.clear();
      }

      N = i;
    }
  }

  if (N > 2) {
    TyInfo.reserve(N - 2);
    for (unsigned j = 2; j < N; ++j)
      TyInfo.push_back(ExtractTypeInfo(I.getArgOperand(j)));
    MMI->addCatchTypeInfo(MBB, TyInfo);
  }
}

void llvm::CopyCatchInfo(const BasicBlock *SrcBB, const BasicBlock *DestBB,
                         MachineModuleInfo *MMI, FunctionLoweringInfo &FLI) {
  for (BasicBlock::const_iterator I = SrcBB->begin(), E = --SrcBB->end();
       I != E; ++I)
    if (const EHSelectorInst *EHSel = dyn_cast<EHSelectorInst>(I)) {
      // Apply the catch info to DestBB.
      AddCatchInfo(*EHSel, MMI, FLI.MBBMap[DestBB]);
#ifndef NDEBUG
      if (!FLI.MBBMap[SrcBB]->isLandingPad())
        FLI.CatchInfoFound.insert(EHSel);
#endif
    }
}
<commit_msg>cache result of operator*<commit_after>//===-- FunctionLoweringInfo.cpp ------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This implements routines for translating functions from LLVM IR into
// Machine IR.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "function-lowering-info"
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/IntrinsicInst.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Target/TargetFrameInfo.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetLowering.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include <algorithm>
using namespace llvm;

/// isUsedOutsideOfDefiningBlock - Return true if this instruction is used by
/// PHI nodes or outside of the basic block that defines it, or used by a
/// switch or atomic instruction, which may expand to multiple basic blocks.
static bool isUsedOutsideOfDefiningBlock(const Instruction *I) {
  if (I->use_empty()) return false;
  if (isa<PHINode>(I)) return true;
  const BasicBlock *BB = I->getParent();
  for (Value::const_use_iterator UI = I->use_begin(), E = I->use_end();
        UI != E; ++UI) {
    const User *U = *UI;
    if (cast<Instruction>(U)->getParent() != BB || isa<PHINode>(U))
      return true;
  }
  return false;
}

/// isOnlyUsedInEntryBlock - If the specified argument is only used in the
/// entry block, return true.  This includes arguments used by switches, since
/// the switch may expand into multiple basic blocks.
static bool isOnlyUsedInEntryBlock(const Argument *A, bool EnableFastISel) {
  // With FastISel active, we may be splitting blocks, so force creation
  // of virtual registers for all non-dead arguments.
  if (EnableFastISel)
    return A->use_empty();

  const BasicBlock *Entry = A->getParent()->begin();
  for (Value::const_use_iterator UI = A->use_begin(), E = A->use_end();
       UI != E; ++UI) {
    const User *U = *UI;
    if (cast<Instruction>(U)->getParent() != Entry || isa<SwitchInst>(U))
      return false;  // Use not in entry block.
  }
  return true;
}

FunctionLoweringInfo::FunctionLoweringInfo(const TargetLowering &tli)
  : TLI(tli) {
}

void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf) {
  Fn = &fn;
  MF = &mf;
  RegInfo = &MF->getRegInfo();

  // Check whether the function can return without sret-demotion.
  SmallVector<ISD::OutputArg, 4> Outs;
  GetReturnInfo(Fn->getReturnType(),
                Fn->getAttributes().getRetAttributes(), Outs, TLI);
  CanLowerReturn = TLI.CanLowerReturn(Fn->getCallingConv(), Fn->isVarArg(),
                                      Outs, Fn->getContext());

  // Create a vreg for each argument register that is not dead and is used
  // outside of the entry block for the function.
  for (Function::const_arg_iterator AI = Fn->arg_begin(), E = Fn->arg_end();
       AI != E; ++AI)
    if (!isOnlyUsedInEntryBlock(AI, EnableFastISel))
      InitializeRegForValue(AI);

  // Initialize the mapping of values to registers.  This is only set up for
  // instruction values that are used outside of the block that defines
  // them.
  Function::const_iterator BB = Fn->begin(), EB = Fn->end();
  for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); I != E; ++I)
    if (const AllocaInst *AI = dyn_cast<AllocaInst>(I))
      if (const ConstantInt *CUI = dyn_cast<ConstantInt>(AI->getArraySize())) {
        const Type *Ty = AI->getAllocatedType();
        uint64_t TySize = TLI.getTargetData()->getTypeAllocSize(Ty);
        unsigned Align =
          std::max((unsigned)TLI.getTargetData()->getPrefTypeAlignment(Ty),
                   AI->getAlignment());

        TySize *= CUI->getZExtValue();   // Get total allocated size.
        if (TySize == 0) TySize = 1; // Don't create zero-sized stack objects.
        StaticAllocaMap[AI] =
          MF->getFrameInfo()->CreateStackObject(TySize, Align, false);
      }

  for (; BB != EB; ++BB)
    for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); I != E; ++I)
      if (isUsedOutsideOfDefiningBlock(I))
        if (!isa<AllocaInst>(I) ||
            !StaticAllocaMap.count(cast<AllocaInst>(I)))
          InitializeRegForValue(I);

  // Create an initial MachineBasicBlock for each LLVM BasicBlock in F.  This
  // also creates the initial PHI MachineInstrs, though none of the input
  // operands are populated.
  for (BB = Fn->begin(); BB != EB; ++BB) {
    MachineBasicBlock *MBB = mf.CreateMachineBasicBlock(BB);
    MBBMap[BB] = MBB;
    MF->push_back(MBB);

    // Transfer the address-taken flag. This is necessary because there could
    // be multiple MachineBasicBlocks corresponding to one BasicBlock, and only
    // the first one should be marked.
    if (BB->hasAddressTaken())
      MBB->setHasAddressTaken();

    // Create Machine PHI nodes for LLVM PHI nodes, lowering them as
    // appropriate.
    for (BasicBlock::const_iterator I = BB->begin();
         const PHINode *PN = dyn_cast<PHINode>(I); ++I) {
      if (PN->use_empty()) continue;

      DebugLoc DL = PN->getDebugLoc();
      unsigned PHIReg = ValueMap[PN];
      assert(PHIReg && "PHI node does not have an assigned virtual register!");

      SmallVector<EVT, 4> ValueVTs;
      ComputeValueVTs(TLI, PN->getType(), ValueVTs);
      for (unsigned vti = 0, vte = ValueVTs.size(); vti != vte; ++vti) {
        EVT VT = ValueVTs[vti];
        unsigned NumRegisters = TLI.getNumRegisters(Fn->getContext(), VT);
        const TargetInstrInfo *TII = MF->getTarget().getInstrInfo();
        for (unsigned i = 0; i != NumRegisters; ++i)
          BuildMI(MBB, DL, TII->get(TargetOpcode::PHI), PHIReg + i);
        PHIReg += NumRegisters;
      }
    }
  }

  // Mark landing pad blocks.
  for (BB = Fn->begin(); BB != EB; ++BB)
    if (const InvokeInst *Invoke = dyn_cast<InvokeInst>(BB->getTerminator()))
      MBBMap[Invoke->getSuccessor(1)]->setIsLandingPad();
}

/// clear - Clear out all the function-specific state. This returns this
/// FunctionLoweringInfo to an empty state, ready to be used for a
/// different function.
void FunctionLoweringInfo::clear() {
  assert(CatchInfoFound.size() == CatchInfoLost.size() &&
         "Not all catch info was assigned to a landing pad!");

  MBBMap.clear();
  ValueMap.clear();
  StaticAllocaMap.clear();
#ifndef NDEBUG
  CatchInfoLost.clear();
  CatchInfoFound.clear();
#endif
  LiveOutRegInfo.clear();
  ArgDbgValues.clear();
  RegFixups.clear();
}

/// CreateReg - Allocate a single virtual register for the given type.
unsigned FunctionLoweringInfo::CreateReg(EVT VT) {
  return RegInfo->createVirtualRegister(TLI.getRegClassFor(VT));
}

/// CreateRegs - Allocate the appropriate number of virtual registers of
/// the correctly promoted or expanded types.  Assign these registers
/// consecutive vreg numbers and return the first assigned number.
///
/// In the case that the given value has struct or array type, this function
/// will assign registers for each member or element.
///
unsigned FunctionLoweringInfo::CreateRegs(const Type *Ty) {
  SmallVector<EVT, 4> ValueVTs;
  ComputeValueVTs(TLI, Ty, ValueVTs);

  unsigned FirstReg = 0;
  for (unsigned Value = 0, e = ValueVTs.size(); Value != e; ++Value) {
    EVT ValueVT = ValueVTs[Value];
    EVT RegisterVT = TLI.getRegisterType(Ty->getContext(), ValueVT);

    unsigned NumRegs = TLI.getNumRegisters(Ty->getContext(), ValueVT);
    for (unsigned i = 0; i != NumRegs; ++i) {
      unsigned R = CreateReg(RegisterVT);
      if (!FirstReg) FirstReg = R;
    }
  }
  return FirstReg;
}

/// AddCatchInfo - Extract the personality and type infos from an eh.selector
/// call, and add them to the specified machine basic block.
void llvm::AddCatchInfo(const CallInst &I, MachineModuleInfo *MMI,
                        MachineBasicBlock *MBB) {
  // Inform the MachineModuleInfo of the personality for this landing pad.
  const ConstantExpr *CE = cast<ConstantExpr>(I.getArgOperand(1));
  assert(CE->getOpcode() == Instruction::BitCast &&
         isa<Function>(CE->getOperand(0)) &&
         "Personality should be a function");
  MMI->addPersonality(MBB, cast<Function>(CE->getOperand(0)));

  // Gather all the type infos for this landing pad and pass them along to
  // MachineModuleInfo.
  std::vector<const GlobalVariable *> TyInfo;
  unsigned N = I.getNumArgOperands();

  for (unsigned i = N - 1; i > 1; --i) {
    if (const ConstantInt *CI = dyn_cast<ConstantInt>(I.getArgOperand(i))) {
      unsigned FilterLength = CI->getZExtValue();
      unsigned FirstCatch = i + FilterLength + !FilterLength;
      assert(FirstCatch <= N && "Invalid filter length");

      if (FirstCatch < N) {
        TyInfo.reserve(N - FirstCatch);
        for (unsigned j = FirstCatch; j < N; ++j)
          TyInfo.push_back(ExtractTypeInfo(I.getArgOperand(j)));
        MMI->addCatchTypeInfo(MBB, TyInfo);
        TyInfo.clear();
      }

      if (!FilterLength) {
        // Cleanup.
        MMI->addCleanup(MBB);
      } else {
        // Filter.
        TyInfo.reserve(FilterLength - 1);
        for (unsigned j = i + 1; j < FirstCatch; ++j)
          TyInfo.push_back(ExtractTypeInfo(I.getArgOperand(j)));
        MMI->addFilterTypeInfo(MBB, TyInfo);
        TyInfo.clear();
      }

      N = i;
    }
  }

  if (N > 2) {
    TyInfo.reserve(N - 2);
    for (unsigned j = 2; j < N; ++j)
      TyInfo.push_back(ExtractTypeInfo(I.getArgOperand(j)));
    MMI->addCatchTypeInfo(MBB, TyInfo);
  }
}

void llvm::CopyCatchInfo(const BasicBlock *SrcBB, const BasicBlock *DestBB,
                         MachineModuleInfo *MMI, FunctionLoweringInfo &FLI) {
  for (BasicBlock::const_iterator I = SrcBB->begin(), E = --SrcBB->end();
       I != E; ++I)
    if (const EHSelectorInst *EHSel = dyn_cast<EHSelectorInst>(I)) {
      // Apply the catch info to DestBB.
      AddCatchInfo(*EHSel, MMI, FLI.MBBMap[DestBB]);
#ifndef NDEBUG
      if (!FLI.MBBMap[SrcBB]->isLandingPad())
        FLI.CatchInfoFound.insert(EHSel);
#endif
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
#include "net/third_party/quiche/src/quic/quartc/test/bidi_test_runner.h"
#include "net/third_party/quiche/src/quic/quartc/test/quic_trace_interceptor.h"
#include "net/third_party/quiche/src/quic/quartc/test/random_packet_filter.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"

namespace quic {
namespace test {
namespace {

class CompetingTransferAlarmDelegate : public quic::QuicAlarm::Delegate {
 public:
  CompetingTransferAlarmDelegate(quic::simulator::QuicEndpoint* endpoint,
                                 QuicByteCount bytes)
      : endpoint_(endpoint), bytes_(bytes) {}

  void OnAlarm() override { endpoint_->AddBytesToTransfer(bytes_); }

 private:
  quic::simulator::QuicEndpoint* const endpoint_;
  const QuicByteCount bytes_;
};

class QuartcBidiTest : public QuicTest {
 protected:
  QuartcBidiTest() {
    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
    QUIC_LOG(INFO) << "Setting random seed to " << seed;
    random_.set_seed(seed);
    simulator_.set_random_generator(&random_);

    client_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("client");
    server_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("server");
  }

  void CreateTransports(QuicBandwidth bandwidth,
                        QuicTime::Delta propagation_delay,
                        QuicByteCount queue_length,
                        int loss_percent) {
    // Endpoints which serve as the transports for client and server.
    client_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "client_transport", "server_transport", queue_length);
    server_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "server_transport", "client_transport", queue_length);

    // Filters on each of the endpoints facilitate random packet loss.
    client_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "client_filter", client_transport_.get());
    server_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "server_filter", server_transport_.get());
    client_filter_->set_loss_percent(loss_percent);
    server_filter_->set_loss_percent(loss_percent);

    // Each endpoint connects directly to a switch.
    client_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "client_switch", /*port_count=*/8, 2 * queue_length);
    server_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "server_switch", /*port_count=*/8, 2 * queue_length);

    // Links to the switch have significantly higher bandwdith than the
    // bottleneck and insignificant propagation delay.
    client_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_filter_.get(), client_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        server_filter_.get(), server_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));

    // The bottleneck link connects the two switches with the bandwidth and
    // propagation delay specified by the test case.
    bottleneck_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_switch_->port(2), server_switch_->port(2), bandwidth,
        propagation_delay);
  }

  void SetupCompetingEndpoints(QuicBandwidth bandwidth) {
    competing_client_ = absl::make_unique<quic::simulator::QuicEndpoint>(
        &simulator_, "competing_client", "competing_server",
        quic::Perspective::IS_CLIENT, quic::test::TestConnectionId(3));
    competing_server_ = absl::make_unique<quic::simulator::QuicEndpoint>(
        &simulator_, "competing_server", "competing_client",
        quic::Perspective::IS_SERVER, quic::test::TestConnectionId(3));

    competing_client_link_ = absl::make_unique<quic::simulator::SymmetricLink>(
        competing_client_.get(), client_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    competing_server_link_ = absl::make_unique<quic::simulator::SymmetricLink>(
        competing_server_.get(), server_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
  }

  simulator::Simulator simulator_;
  SimpleRandom random_;

  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
  std::unique_ptr<simulator::RandomPacketFilter> client_filter_;
  std::unique_ptr<simulator::RandomPacketFilter> server_filter_;
  std::unique_ptr<simulator::Switch> client_switch_;
  std::unique_ptr<simulator::Switch> server_switch_;
  std::unique_ptr<simulator::SymmetricLink> client_link_;
  std::unique_ptr<simulator::SymmetricLink> server_link_;
  std::unique_ptr<simulator::SymmetricLink> bottleneck_link_;

  std::unique_ptr<simulator::QuicEndpoint> competing_client_;
  std::unique_ptr<simulator::QuicEndpoint> competing_server_;
  std::unique_ptr<simulator::SymmetricLink> competing_client_link_;
  std::unique_ptr<simulator::SymmetricLink> competing_server_link_;

  std::unique_ptr<QuicTraceInterceptor> client_trace_interceptor_;
  std::unique_ptr<QuicTraceInterceptor> server_trace_interceptor_;
};

TEST_F(QuartcBidiTest, Basic300kbps200ms) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/0);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLoss) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/2);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLossCompetingBurst) {
  QuicBandwidth bandwidth = QuicBandwidth::FromKBitsPerSecond(300);
  CreateTransports(bandwidth, QuicTime::Delta::FromMilliseconds(200),
                   10 * quic::kDefaultMaxPacketSize, /*loss_percent=*/2);
  SetupCompetingEndpoints(bandwidth);

  QuicTime competing_burst_time =
      simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(15);
  std::unique_ptr<quic::QuicAlarm> competing_client_burst_alarm_ =
      absl::WrapUnique(simulator_.GetAlarmFactory()->CreateAlarm(
          new CompetingTransferAlarmDelegate(competing_client_.get(),
                                             /*bytes=*/50 * 1024)));
  std::unique_ptr<quic::QuicAlarm> competing_server_burst_alarm_ =
      absl::WrapUnique(simulator_.GetAlarmFactory()->CreateAlarm(
          new CompetingTransferAlarmDelegate(competing_server_.get(),
                                             /*bytes=*/50 * 1024)));
  competing_client_burst_alarm_->Set(competing_burst_time);
  competing_server_burst_alarm_->Set(competing_burst_time);

  quic::test::BidiTestRunner runner(&simulator_, client_transport_.get(),
                                    server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

}  // namespace
}  // namespace test
}  // namespace quic
<commit_msg>Use QuicMakeUnique and QuicWrapUnique in quartc_bidi_test.cc.<commit_after>// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h"
#include "net/third_party/quiche/src/quic/core/quic_time.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
#include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h"
#include "net/third_party/quiche/src/quic/quartc/test/bidi_test_runner.h"
#include "net/third_party/quiche/src/quic/quartc/test/quic_trace_interceptor.h"
#include "net/third_party/quiche/src/quic/quartc/test/random_packet_filter.h"
#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/link.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/quic_endpoint.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h"
#include "net/third_party/quiche/src/quic/test_tools/simulator/switch.h"

namespace quic {
namespace test {
namespace {

class CompetingTransferAlarmDelegate : public quic::QuicAlarm::Delegate {
 public:
  CompetingTransferAlarmDelegate(quic::simulator::QuicEndpoint* endpoint,
                                 QuicByteCount bytes)
      : endpoint_(endpoint), bytes_(bytes) {}

  void OnAlarm() override { endpoint_->AddBytesToTransfer(bytes_); }

 private:
  quic::simulator::QuicEndpoint* const endpoint_;
  const QuicByteCount bytes_;
};

class QuartcBidiTest : public QuicTest {
 protected:
  QuartcBidiTest() {
    uint64_t seed = QuicRandom::GetInstance()->RandUint64();
    QUIC_LOG(INFO) << "Setting random seed to " << seed;
    random_.set_seed(seed);
    simulator_.set_random_generator(&random_);

    client_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("client");
    server_trace_interceptor_ = QuicMakeUnique<QuicTraceInterceptor>("server");
  }

  void CreateTransports(QuicBandwidth bandwidth,
                        QuicTime::Delta propagation_delay,
                        QuicByteCount queue_length,
                        int loss_percent) {
    // Endpoints which serve as the transports for client and server.
    client_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "client_transport", "server_transport", queue_length);
    server_transport_ =
        QuicMakeUnique<simulator::SimulatedQuartcPacketTransport>(
            &simulator_, "server_transport", "client_transport", queue_length);

    // Filters on each of the endpoints facilitate random packet loss.
    client_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "client_filter", client_transport_.get());
    server_filter_ = QuicMakeUnique<simulator::RandomPacketFilter>(
        &simulator_, "server_filter", server_transport_.get());
    client_filter_->set_loss_percent(loss_percent);
    server_filter_->set_loss_percent(loss_percent);

    // Each endpoint connects directly to a switch.
    client_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "client_switch", /*port_count=*/8, 2 * queue_length);
    server_switch_ = QuicMakeUnique<simulator::Switch>(
        &simulator_, "server_switch", /*port_count=*/8, 2 * queue_length);

    // Links to the switch have significantly higher bandwdith than the
    // bottleneck and insignificant propagation delay.
    client_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_filter_.get(), client_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    server_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        server_filter_.get(), server_switch_->port(1), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));

    // The bottleneck link connects the two switches with the bandwidth and
    // propagation delay specified by the test case.
    bottleneck_link_ = QuicMakeUnique<simulator::SymmetricLink>(
        client_switch_->port(2), server_switch_->port(2), bandwidth,
        propagation_delay);
  }

  void SetupCompetingEndpoints(QuicBandwidth bandwidth) {
    competing_client_ = QuicMakeUnique<quic::simulator::QuicEndpoint>(
        &simulator_, "competing_client", "competing_server",
        quic::Perspective::IS_CLIENT, quic::test::TestConnectionId(3));
    competing_server_ = QuicMakeUnique<quic::simulator::QuicEndpoint>(
        &simulator_, "competing_server", "competing_client",
        quic::Perspective::IS_SERVER, quic::test::TestConnectionId(3));

    competing_client_link_ = QuicMakeUnique<quic::simulator::SymmetricLink>(
        competing_client_.get(), client_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
    competing_server_link_ = QuicMakeUnique<quic::simulator::SymmetricLink>(
        competing_server_.get(), server_switch_->port(3), 10 * bandwidth,
        QuicTime::Delta::FromMicroseconds(1));
  }

  simulator::Simulator simulator_;
  SimpleRandom random_;

  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_;
  std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_;
  std::unique_ptr<simulator::RandomPacketFilter> client_filter_;
  std::unique_ptr<simulator::RandomPacketFilter> server_filter_;
  std::unique_ptr<simulator::Switch> client_switch_;
  std::unique_ptr<simulator::Switch> server_switch_;
  std::unique_ptr<simulator::SymmetricLink> client_link_;
  std::unique_ptr<simulator::SymmetricLink> server_link_;
  std::unique_ptr<simulator::SymmetricLink> bottleneck_link_;

  std::unique_ptr<simulator::QuicEndpoint> competing_client_;
  std::unique_ptr<simulator::QuicEndpoint> competing_server_;
  std::unique_ptr<simulator::SymmetricLink> competing_client_link_;
  std::unique_ptr<simulator::SymmetricLink> competing_server_link_;

  std::unique_ptr<QuicTraceInterceptor> client_trace_interceptor_;
  std::unique_ptr<QuicTraceInterceptor> server_trace_interceptor_;
};

TEST_F(QuartcBidiTest, Basic300kbps200ms) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/0);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLoss) {
  CreateTransports(QuicBandwidth::FromKBitsPerSecond(300),
                   QuicTime::Delta::FromMilliseconds(200),
                   10 * kDefaultMaxPacketSize, /*loss_percent=*/2);
  BidiTestRunner runner(&simulator_, client_transport_.get(),
                        server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

TEST_F(QuartcBidiTest, 300kbps200ms2PercentLossCompetingBurst) {
  QuicBandwidth bandwidth = QuicBandwidth::FromKBitsPerSecond(300);
  CreateTransports(bandwidth, QuicTime::Delta::FromMilliseconds(200),
                   10 * quic::kDefaultMaxPacketSize, /*loss_percent=*/2);
  SetupCompetingEndpoints(bandwidth);

  QuicTime competing_burst_time =
      simulator_.GetClock()->Now() + QuicTime::Delta::FromSeconds(15);
  std::unique_ptr<quic::QuicAlarm> competing_client_burst_alarm_ =
      QuicWrapUnique(simulator_.GetAlarmFactory()->CreateAlarm(
          new CompetingTransferAlarmDelegate(competing_client_.get(),
                                             /*bytes=*/50 * 1024)));
  std::unique_ptr<quic::QuicAlarm> competing_server_burst_alarm_ =
      QuicWrapUnique(simulator_.GetAlarmFactory()->CreateAlarm(
          new CompetingTransferAlarmDelegate(competing_server_.get(),
                                             /*bytes=*/50 * 1024)));
  competing_client_burst_alarm_->Set(competing_burst_time);
  competing_server_burst_alarm_->Set(competing_burst_time);

  quic::test::BidiTestRunner runner(&simulator_, client_transport_.get(),
                                    server_transport_.get());
  runner.set_client_interceptor(client_trace_interceptor_.get());
  runner.set_server_interceptor(server_trace_interceptor_.get());
  EXPECT_TRUE(runner.RunTest(QuicTime::Delta::FromSeconds(30)));
}

}  // namespace
}  // namespace test
}  // namespace quic
<|endoftext|>


<commit_before>#include "test_compiler.hpp"

#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cstdlib>
#include <regex>
#include <sstream>
#include <thread>

#include <mettle/driver/scoped_pipe.hpp>
#include <mettle/output.hpp>

namespace caliber {

namespace detail {

  std::unique_ptr<char *[]>
  make_argv(const std::vector<std::string> &argv) {
    auto real_argv = std::make_unique<char *[]>(argv.size() + 1);
    for(size_t i = 0; i != argv.size(); i++)
      real_argv[i] = const_cast<char*>(argv[i].c_str());
    return real_argv;
  }

  inline std::string err_string(int errnum) {
    char buf[256];
#ifdef _GNU_SOURCE
    return strerror_r(errnum, buf, sizeof(buf));
#else
    if(strerror_r(errnum, buf, sizeof(buf)) < 0)
      return "";
    return buf;
#endif
  }

  inline mettle::test_result parent_failed() {
    return { false, err_string(errno) };
  }

  [[noreturn]] inline void child_failed() {
    _exit(128);
  }
}

mettle::test_result
test_compiler::operator ()(
  const std::string &file, const args_type &args, bool expect_fail,
  mettle::log::test_output &output
) const {
  using namespace detail;

  mettle::scoped_pipe stdout_pipe, stderr_pipe;
  if(stdout_pipe.open() < 0 ||
     stderr_pipe.open() < 0)
    return parent_failed();

  fflush(nullptr);

  const auto &compiler = is_cxx(file) ? cxx_ : cc_;
  std::vector<std::string> final_args = {
    compiler.c_str(), "-fsyntax-only", file
  };
  for(const auto &i : args) {
    final_args.push_back(i.string_key);
    final_args.insert(final_args.end(), i.value.begin(), i.value.end());
  }

  pid_t pid;
  if((pid = fork()) < 0)
    return parent_failed();

  if(pid == 0) {
    if(timeout_)
      fork_watcher(*timeout_);

    if(stdout_pipe.close_read() < 0 ||
       stderr_pipe.close_read() < 0)
      child_failed();

    if(dup2(stdout_pipe.write_fd, STDOUT_FILENO) < 0 ||
       dup2(stderr_pipe.write_fd, STDERR_FILENO) < 0)
      child_failed();

    if(stdout_pipe.close_write() < 0 ||
       stderr_pipe.close_write() < 0)
      child_failed();

    execvp(compiler.c_str(), make_argv(final_args).get());
    child_failed();
  }
  else {
    if(stdout_pipe.close_write() < 0 ||
       stderr_pipe.close_write() < 0)
      return parent_failed();

    ssize_t size;
    char buf[BUFSIZ];

    // Read from the piped stdout and stderr.
    int rv;
    pollfd fds[2] = { {stdout_pipe.read_fd, POLLIN, 0},
                      {stderr_pipe.read_fd, POLLIN, 0} };
    std::string *dests[] = {&output.stdout, &output.stderr};
    int open_fds = 2;
    while(open_fds && (rv = poll(fds, 2, -1)) > 0) {
      for(size_t i = 0; i < 2; i++) {
        if(fds[i].revents & POLLIN) {
          if((size = read(fds[i].fd, buf, sizeof(buf))) < 0)
            return parent_failed();
          dests[i]->append(buf, size);
        }
        if(fds[i].revents & POLLHUP) {
          fds[i].fd = -fds[i].fd;
          open_fds--;
        }
      }
    }
    if(rv < 0) // poll() failed!
      return parent_failed();

    int status;
    if(waitpid(pid, &status, 0) < 0)
      return parent_failed();

    if(WIFEXITED(status)) {
      int exit_code = WEXITSTATUS(status);
      if(exit_code == 2) {
        std::ostringstream ss;
        ss << "Timed out after " << timeout_->count() << " ms";
        return { false, ss.str() };
      }
      else if(bool(exit_code) == expect_fail) {
        return { true, "" };
      }
      else if(exit_code) {
        return { false, "Compilation failed" };
      }
      else {
        return { false, "Compilation successful" };
      }
    }
    else if(WIFSIGNALED(status)) {
      return { false, strsignal(WTERMSIG(status)) };
    }
    else { // WIFSTOPPED
      return { false, "Stopped" };
    }
  }
}

bool test_compiler::is_cxx(const std::string &file) {
  static std::regex cxx_re("\\.(cc|cp|cxx|cpp|CPP|c\\+\\+|C|ii)$");
  return std::regex_search(file, cxx_re);
}

void test_compiler::fork_watcher(std::chrono::milliseconds timeout) {
  pid_t watcher_pid;
  if((watcher_pid = fork()) < 0)
    goto fail;
  if(watcher_pid == 0) {
    std::this_thread::sleep_for(timeout);
    _exit(2);
  }

  pid_t test_pid;
  if((test_pid = fork()) < 0) {
    kill(watcher_pid, SIGKILL);
    goto fail;
  }
  if(test_pid != 0) {
    // Wait for the first child process (the watcher or the test) to finish,
    // and kill the other one.
    int status;
    pid_t exited_pid = wait(&status);
    kill(exited_pid == test_pid ? watcher_pid : test_pid, SIGKILL);
    wait(nullptr);

    if(WIFEXITED(status))
      _exit(WEXITSTATUS(status));
    else if(WIFSIGNALED(status))
      raise(WTERMSIG(status));
    else // WIFSTOPPED
      _exit(128); // XXX: not sure what to do here
  }

  return;
fail:
  _exit(128);
}

} // namespace caliber
<commit_msg>Use a constant for the timeout exit status<commit_after>#include "test_compiler.hpp"

#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cstdlib>
#include <regex>
#include <sstream>
#include <thread>

#include <mettle/driver/scoped_pipe.hpp>
#include <mettle/output.hpp>

namespace caliber {

namespace {
  static constexpr int err_timeout = 64;
}

std::unique_ptr<char *[]>
make_argv(const std::vector<std::string> &argv) {
  auto real_argv = std::make_unique<char *[]>(argv.size() + 1);
  for(size_t i = 0; i != argv.size(); i++)
    real_argv[i] = const_cast<char*>(argv[i].c_str());
  return real_argv;
}

inline std::string err_string(int errnum) {
  char buf[256];
#ifdef _GNU_SOURCE
  return strerror_r(errnum, buf, sizeof(buf));
#else
  if(strerror_r(errnum, buf, sizeof(buf)) < 0)
    return "";
  return buf;
#endif
}

inline mettle::test_result parent_failed() {
  return { false, err_string(errno) };
}

[[noreturn]] inline void child_failed() {
  _exit(128);
}

mettle::test_result
test_compiler::operator ()(
  const std::string &file, const args_type &args, bool expect_fail,
  mettle::log::test_output &output
) const {
  mettle::scoped_pipe stdout_pipe, stderr_pipe;
  if(stdout_pipe.open() < 0 ||
     stderr_pipe.open() < 0)
    return parent_failed();

  fflush(nullptr);

  const auto &compiler = is_cxx(file) ? cxx_ : cc_;
  std::vector<std::string> final_args = {
    compiler.c_str(), "-fsyntax-only", file
  };
  for(const auto &i : args) {
    final_args.push_back(i.string_key);
    final_args.insert(final_args.end(), i.value.begin(), i.value.end());
  }

  pid_t pid;
  if((pid = fork()) < 0)
    return parent_failed();

  if(pid == 0) {
    if(timeout_)
      fork_watcher(*timeout_);

    if(stdout_pipe.close_read() < 0 ||
       stderr_pipe.close_read() < 0)
      child_failed();

    if(dup2(stdout_pipe.write_fd, STDOUT_FILENO) < 0 ||
       dup2(stderr_pipe.write_fd, STDERR_FILENO) < 0)
      child_failed();

    if(stdout_pipe.close_write() < 0 ||
       stderr_pipe.close_write() < 0)
      child_failed();

    execvp(compiler.c_str(), make_argv(final_args).get());
    child_failed();
  }
  else {
    if(stdout_pipe.close_write() < 0 ||
       stderr_pipe.close_write() < 0)
      return parent_failed();

    ssize_t size;
    char buf[BUFSIZ];

    // Read from the piped stdout and stderr.
    int rv;
    pollfd fds[2] = { {stdout_pipe.read_fd, POLLIN, 0},
                      {stderr_pipe.read_fd, POLLIN, 0} };
    std::string *dests[] = {&output.stdout, &output.stderr};
    int open_fds = 2;
    while(open_fds && (rv = poll(fds, 2, -1)) > 0) {
      for(size_t i = 0; i < 2; i++) {
        if(fds[i].revents & POLLIN) {
          if((size = read(fds[i].fd, buf, sizeof(buf))) < 0)
            return parent_failed();
          dests[i]->append(buf, size);
        }
        if(fds[i].revents & POLLHUP) {
          fds[i].fd = -fds[i].fd;
          open_fds--;
        }
      }
    }
    if(rv < 0) // poll() failed!
      return parent_failed();

    int status;
    if(waitpid(pid, &status, 0) < 0)
      return parent_failed();

    if(WIFEXITED(status)) {
      int exit_code = WEXITSTATUS(status);
      if(exit_code == err_timeout) {
        std::ostringstream ss;
        ss << "Timed out after " << timeout_->count() << " ms";
        return { false, ss.str() };
      }
      else if(bool(exit_code) == expect_fail) {
        return { true, "" };
      }
      else if(exit_code) {
        return { false, "Compilation failed" };
      }
      else {
        return { false, "Compilation successful" };
      }
    }
    else if(WIFSIGNALED(status)) {
      return { false, strsignal(WTERMSIG(status)) };
    }
    else { // WIFSTOPPED
      return { false, "Stopped" };
    }
  }
}

bool test_compiler::is_cxx(const std::string &file) {
  static std::regex cxx_re("\\.(cc|cp|cxx|cpp|CPP|c\\+\\+|C|ii)$");
  return std::regex_search(file, cxx_re);
}

void test_compiler::fork_watcher(std::chrono::milliseconds timeout) {
  pid_t watcher_pid;
  if((watcher_pid = fork()) < 0)
    goto fail;
  if(watcher_pid == 0) {
    std::this_thread::sleep_for(timeout);
    _exit(err_timeout);
  }

  pid_t test_pid;
  if((test_pid = fork()) < 0) {
    kill(watcher_pid, SIGKILL);
    goto fail;
  }
  if(test_pid != 0) {
    // Wait for the first child process (the watcher or the test) to finish,
    // and kill the other one.
    int status;
    pid_t exited_pid = wait(&status);
    kill(exited_pid == test_pid ? watcher_pid : test_pid, SIGKILL);
    wait(nullptr);

    if(WIFEXITED(status))
      _exit(WEXITSTATUS(status));
    else if(WIFSIGNALED(status))
      raise(WTERMSIG(status));
    else // WIFSTOPPED
      _exit(128); // XXX: not sure what to do here
  }

  return;
fail:
  _exit(128);
}

} // namespace caliber
<|endoftext|>


<commit_before>#include"ros/ros.h"
#include"geometry_msgs/Pose.h"
#include"tf/transform_listener.h"
#include"move_base_msgs/MoveBaseGoal.h"
#include"move_base_msgs/MoveBaseAction.h"
#include"actionlib/client/simple_action_client.h"

#include<utility>
#include<vector>
#include<stdexcept>
#include<string>
#include<fstream> // for csv
#include<sstream> // ditto

struct Waypoint;
using WaypointContainer = std::vector<Waypoint>;
using MoveBaseActionClient = actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>;

geometry_msgs::Pose getFramePose(const std::string&, const std::string&);
double calcDistance(const geometry_msgs::Pose&, const geometry_msgs::Pose&);

struct Waypoint {
  static WaypointContainer readCsv(const std::string&);
  Waypoint(move_base_msgs::MoveBaseGoal, double);

  move_base_msgs::MoveBaseGoal goal;
  double valid_range;
};

class GoalSender {
public:
  GoalSender();
  void run();
private:
  bool checkToNext();
  void sendGoalPoint();

  ros::NodeHandle n;
  ros::NodeHandle pn;
  tf::TransformListener tf_listener;
  WaypointContainer waypoints;
  WaypointContainer::iterator now_waypoint;
  MoveBaseActionClient move_base_client;
};

int main(int argc, char* argv[]){
  ros::init(argc, argv, "goal_sender_node");
  GoalSender goal_sender {};
  ros::Rate rate {10};
  while (ros::ok()) {
    ros::spinOnce();
    goal_sender.run();
    rate.sleep();
  }
  return 0;
}

geometry_msgs::Pose getFramePose(tf::TransformListener& tf, const std::string& parent, const std::string& child) {
  tf::StampedTransform transform;
  try {
    tf.lookupTransform(parent, child, ros::Time(0), transform);
  } catch (const tf::TransformException& e) {
    ROS_ERROR("%s", e.what());
    throw std::runtime_error {"tf cannot resolve"};
  }
  geometry_msgs::Pose pose;
  pose.position.x = transform.getOrigin().x();
  pose.position.y = transform.getOrigin().y();
  return pose;
}

inline double calcDistance(const geometry_msgs::Pose& a, const geometry_msgs::Pose& b) {
  return sqrt(pow((a.position.x - b.position.x), 2.0) + pow((a.position.y - b.position.y), 2.0));
}

WaypointContainer Waypoint::readCsv(const std::string& path) {
  if (path.empty()) {
    ROS_ERROR("I need path of waypoint");
    throw std::invalid_argument {"no exist file"};
  }
  std::ifstream fs {path}; // input file stream
  if (!fs) throw std::runtime_error {"Cannot open file"};
  std::string line;
  WaypointContainer waypoints;
  while (std::getline(fs, line)) {
    if (line.empty()) break; // skip the empty line
    std::istringstream line_stream {std::move(line)}; // convert to stream
    std::vector<double> input_data;
    auto input_it = back_inserter(input_data);
    std::string oneData;
    while (std::getline(line_stream, oneData, ',')) {
      std::istringstream data_st {std::move(oneData)}; // convert to stream
      double data;
      data_st >> data;
      *input_it = data;
      ++input_it;
    }
    move_base_msgs::MoveBaseGoal goal;
    goal.target_pose.pose.position.x    = input_data[0];
    goal.target_pose.pose.position.y    = input_data[1];
    goal.target_pose.pose.position.z    = input_data[2];
    goal.target_pose.pose.orientation.x = input_data[3];
    goal.target_pose.pose.orientation.y = input_data[4];
    goal.target_pose.pose.orientation.z = input_data[5];
    goal.target_pose.pose.orientation.w = input_data[6];
    goal.target_pose.header.frame_id = "/map";
    waypoints.emplace_back(std::move(goal), input_data[7]);
  }
  return waypoints;
}

inline Waypoint::Waypoint(move_base_msgs::MoveBaseGoal goal, double valid_range)
  : goal {goal},
    valid_range {valid_range}
{}

GoalSender::GoalSender()
  : n {},
    pn {"~"},
    tf_listener {},
    waypoints {},
    now_waypoint {waypoints.begin()},
    move_base_client {"move_base", true}
{
  std::string path;
  pn.getParam("path", path);
  try {
    waypoints = Waypoint::readCsv(path); // throw std::invalid_argument, std::runtime_error
  } catch (const std::runtime_error& e) {
    ROS_ERROR("%s [%s]", e.what(), path.c_str());
    throw;
  }
  move_base_client.waitForServer();
  sendGoalPoint(); // set first waypoint
}

inline void GoalSender::run() {
  if (checkToNext()) sendGoalPoint(); // send only when cange waypoint
}

bool GoalSender::checkToNext() {
  auto robot_pos = getFramePose(tf_listener, "/map", "/base_link");
  auto waypoint_pos = now_waypoint->goal.target_pose.pose;
  auto distance = calcDistance(robot_pos, waypoint_pos); // distance of between robot and target
  if (distance < now_waypoint->valid_range) { // into range
    ++now_waypoint; // next waypoint
    return true;
  }
  return false;
}

void GoalSender::sendGoalPoint() {
  if (now_waypoint  == waypoints.end()) { // finish waypoint
    move_base_client.cancelGoal(); // cancel moveing
    ROS_INFO("Finish waypoints");
    return;
  }
  now_waypoint->goal.target_pose.header.stamp = ros::Time::now(); // others writed by Waypoint class
  move_base_client.sendGoal(now_waypoint->goal); // send waypoint
  ROS_INFO("Use waypoint [%ld]", now_waypoint - waypoints.begin());
}
<commit_msg>Update timing of get param.<commit_after>#include"ros/ros.h"
#include"geometry_msgs/Pose.h"
#include"tf/transform_listener.h"
#include"move_base_msgs/MoveBaseGoal.h"
#include"move_base_msgs/MoveBaseAction.h"
#include"actionlib/client/simple_action_client.h"

#include<utility>
#include<vector>
#include<stdexcept>
#include<string>
#include<fstream> // for csv
#include<sstream> // ditto

struct Waypoint;
using WaypointContainer = std::vector<Waypoint>;
using MoveBaseActionClient = actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>;

geometry_msgs::Pose getFramePose(const std::string&, const std::string&);
double calcDistance(const geometry_msgs::Pose&, const geometry_msgs::Pose&);

struct Waypoint {
  static WaypointContainer readCsv(const std::string&);
  Waypoint(move_base_msgs::MoveBaseGoal, double);

  move_base_msgs::MoveBaseGoal goal;
  double valid_range;
};

class GoalSender {
public:
  GoalSender(const std::string&);
  void run();
private:
  bool checkToNext();
  void sendGoalPoint();

  WaypointContainer waypoints;
  WaypointContainer::iterator now_waypoint;
  tf::TransformListener tf_listener;
  MoveBaseActionClient move_base_client;
};

int main(int argc, char* argv[]){
  ros::init(argc, argv, "goal_sender_node");
  ros::NodeHandle pn {"~"};
  std::string path;
  pn.getParam("path", path);
  GoalSender goal_sender {std::move(path)};
  ros::Rate rate {10};
  while (ros::ok()) {
    ros::spinOnce();
    goal_sender.run();
    rate.sleep();
  }
  return 0;
}

geometry_msgs::Pose getFramePose(tf::TransformListener& tf, const std::string& parent, const std::string& child) {
  tf::StampedTransform transform;
  try {
    tf.lookupTransform(parent, child, ros::Time(0), transform);
  } catch (const tf::TransformException& e) {
    ROS_ERROR("%s", e.what());
    throw std::runtime_error {"tf cannot resolve"};
  }
  geometry_msgs::Pose pose;
  pose.position.x = transform.getOrigin().x();
  pose.position.y = transform.getOrigin().y();
  return pose;
}

inline double calcDistance(const geometry_msgs::Pose& a, const geometry_msgs::Pose& b) {
  return sqrt(pow((a.position.x - b.position.x), 2.0) + pow((a.position.y - b.position.y), 2.0));
}

WaypointContainer Waypoint::readCsv(const std::string& path) {
  if (path.empty()) {
    ROS_ERROR("I need path of waypoint");
    throw std::invalid_argument {"no exist file"};
  }
  std::ifstream fs {path}; // input file stream
  if (!fs) throw std::runtime_error {"Cannot open file"};
  std::string line;
  WaypointContainer waypoints;
  while (std::getline(fs, line)) {
    if (line.empty()) break; // skip the empty line
    std::istringstream line_stream {std::move(line)}; // convert to stream
    std::vector<double> input_data;
    auto input_it = back_inserter(input_data);
    std::string oneData;
    while (std::getline(line_stream, oneData, ',')) {
      std::istringstream data_st {std::move(oneData)}; // convert to stream
      double data;
      data_st >> data;
      *input_it = data;
      ++input_it;
    }
    move_base_msgs::MoveBaseGoal goal;
    goal.target_pose.pose.position.x    = input_data[0];
    goal.target_pose.pose.position.y    = input_data[1];
    goal.target_pose.pose.position.z    = input_data[2];
    goal.target_pose.pose.orientation.x = input_data[3];
    goal.target_pose.pose.orientation.y = input_data[4];
    goal.target_pose.pose.orientation.z = input_data[5];
    goal.target_pose.pose.orientation.w = input_data[6];
    goal.target_pose.header.frame_id = "/map";
    waypoints.emplace_back(std::move(goal), input_data[7]);
  }
  return waypoints;
}

inline Waypoint::Waypoint(move_base_msgs::MoveBaseGoal goal, double valid_range)
  : goal {goal},
    valid_range {valid_range}
{}

GoalSender::GoalSender(const std::string& path)
  : waypoints {Waypoint::readCsv(path)},
    now_waypoint {waypoints.begin()},
    tf_listener {},
    move_base_client {"move_base", true}
{
  move_base_client.waitForServer();
  sendGoalPoint(); // set first waypoint
}

inline void GoalSender::run() {
  if (checkToNext()) sendGoalPoint(); // send only when cange waypoint
}

bool GoalSender::checkToNext() {
  auto robot_pos = getFramePose(tf_listener, "/map", "/base_link");
  auto waypoint_pos = now_waypoint->goal.target_pose.pose;
  auto distance = calcDistance(robot_pos, waypoint_pos); // distance of between robot and target
  if (distance < now_waypoint->valid_range) { // into range
    ++now_waypoint; // next waypoint
    return true;
  }
  return false;
}

void GoalSender::sendGoalPoint() {
  if (now_waypoint  == waypoints.end()) { // finish waypoint
    move_base_client.cancelGoal(); // cancel moveing
    ROS_INFO("Finish waypoints");
    return;
  }
  now_waypoint->goal.target_pose.header.stamp = ros::Time::now(); // others writed by Waypoint class
  move_base_client.sendGoal(now_waypoint->goal); // send waypoint
  ROS_INFO("Use waypoint [%ld]", now_waypoint - waypoints.begin());
}
<|endoftext|>


<commit_before>/* -*-c++-*-
 *
 * Copyright (C) 2008 Stuart Buchanan
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */

#include <algorithm>
#include <string>
#include <map>

#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Matrix>
#include <osg/StateSet>
#include <osg/Texture2D>
#include <osg/TexEnv>

#include <osgDB/ReadFile>
#include <osgDB/FileUtils>

#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>

#include "ShaderGeometry.hxx"
#include "TreeBin.hxx"

#define SG_TREE_QUAD_TREE_DEPTH 3

// Comments from Tim Moore:
// Some work remains for this code. Stuart's enhancement for multiple
// textures per forest should be integrated. We should try to use one
// ShaderGeometry for *all* the trees in the scene graph and do the
// rotation and scale with a MatrixTransform above the trees quad
// tree. The positions would of course have to be transformed by the
// inverse of that transform. Also, we should investigate whether it
// would be better to instantiate trees as polygons in a osg::Geometry
// object instead of using the ShaderGeometry instancing technique.

using namespace osg;

namespace simgear
{

osg::Geometry* createOrthQuads(float w, float h, int varieties, const osg::Matrix& rotate)
{

    //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
    // set up the coords
    // Create front and back polygons so we don't need to screw around
    // with two-sided lighting in the shader.
    osg::Vec3Array& v = *(new osg::Vec3Array(8));
    osg::Vec3Array& n = *(new osg::Vec3Array(8));
    osg::Vec2Array& t = *(new osg::Vec2Array(8));

    float cw = w*0.5f;

    v[0].set(0.0f,-cw,0.0f);
    v[1].set(0.0f, cw,0.0f);
    v[2].set(0.0f, cw,h);
    v[3].set(0.0f,-cw,h);

    v[4].set(-cw,0.0f,0.0f);
    v[5].set( cw,0.0f,0.0f);
    v[6].set( cw,0.0f,h);
    v[7].set(-cw,0.0f,h);

    // The texture coordinate range is not the
    // entire coordinate space - as the texture
    // has a number of different trees on it.
    float tx = 1.0f/varieties;

    t[0].set(0.0f,0.0f);
    t[1].set(  tx,0.0f);
    t[2].set(  tx,1.0f);
    t[3].set(0.0f,1.0f);

    t[4].set(0.0f,0.0f);
    t[5].set(  tx,0.0f);
    t[6].set(  tx,1.0f);
    t[7].set(0.0f,1.0f);

    // For now the normal is normal to the quad. If we want to get
    // fancier and approximate a cylindrical tree or something, then
    // we would really want more geometry.
    std::fill(n.begin(), n.begin() + 4, Vec3f(1.0f, 0.0f, 0.0f));
    std::fill(n.begin() + 4, n.end(), Vec3f(0.0f, -1.0f, 0.0f));
    for (unsigned int i = 0; i < 8; i++) {
        v[i] = v[i] * rotate;
        // Should be the inverse transpose, but assume that rotate is
        // orthonormal.
        n[i] = n[i] * rotate;
    }

    osg::Geometry *geom = new osg::Geometry;

    geom->setVertexArray(&v);
    geom->setTexCoordArray(0, &t);
    geom->setNormalArray(&n);
    geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
    // No color for now; that's used to pass the position.
    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8));

    return geom;
}

 static char vertexShaderSource[] =
    "varying float fogFactor;\n"
#ifndef TSG_PACKED_ATTRIBUTES
    "attribute float textureIndex;\n"
#endif
    "\n"
    "void main(void)\n"
    "{\n"
#ifdef TSG_PACKED_ATTRIBUTES
    "  gl_TexCoord[0] = gl_MultiTexCoord0;\n"
#else
    "  gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndex, 0.0, 0.0, 0.0);\n"
#endif
    "  vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n"
    "  gl_Position   = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
    "  vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(position, 1.0));\n"
    "  vec3 N = normalize(gl_NormalMatrix * gl_Normal);\n"
    "  vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(N, gl_LightSource[0].position.xyz));\n"
    "  vec3 backDiffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(-N, gl_LightSource[0].position.xyz));\n"
    " vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n"
    " gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n"
    " gl_BackColor = ambientColor + gl_LightSource[0].diffuse * vec4(backDiffuse, 1.0)\n;"
//    "  gl_TexCoord[0] = gl_MultiTexCoord0;\n"
    " float fogCoord = abs(ecPosition.z);\n"
    "  fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n"
    "  fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
    "}\n";

static char fragmentShaderSource[] =
    "uniform sampler2D baseTexture; \n"
    "varying float fogFactor;\n"
    "\n"
    "void main(void) \n"
    "{ \n"
    "  vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
    "  vec4 finalColor = base * gl_Color;\n"
    "  gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
    "}\n";

typedef std::map<std::string, osg::ref_ptr<StateSet> > StateSetMap;

static StateSetMap treeTextureMap;

// Helper classes for creating the quad tree
namespace
{
struct MakeTreesLeaf
{
    MakeTreesLeaf(float range, Geometry* geometry, int varieties) :
        _range(range), _geometry(geometry), _varieties(varieties)
    {}
    MakeTreesLeaf(const MakeTreesLeaf& rhs) :
        _range(rhs._range), _geometry(rhs._geometry), _varieties(rhs._varieties) {}
    LOD* operator() () const
    {
        LOD* result = new LOD;
        Geode* geode = new Geode;
        ShaderGeometry* sg = new ShaderGeometry(_varieties);
        sg->setGeometry(_geometry);
        geode->addDrawable(sg);
        result->addChild(geode, 0, _range);
        return result;
    }
    float _range;
    int _varieties;
    Geometry* _geometry;
};

struct AddTreesLeafObject
{
    void operator() (LOD* lod, const TreeBin::Tree& tree) const
    {
        Geode* geode = static_cast<Geode*>(lod->getChild(0));
        ShaderGeometry* sg
            = static_cast<ShaderGeometry*>(geode->getDrawable(0));
        sg->addTree(tree);
    }
};

struct GetTreeCoord
{
    GetTreeCoord(const Matrix& transform) : _transform(transform) {}
    GetTreeCoord(const GetTreeCoord& rhs) : _transform(rhs._transform) {}
    Vec3 operator() (const TreeBin::Tree& tree) const
    {
        return tree.position.osg() * _transform;
    }
    Matrix _transform;
};

typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
                        GetTreeCoord> ShaderGeometryQuadtree;
}

osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
{
    // Set up some shared structures.
    osg::Geometry* shared_geometry = createOrthQuads(forest.width,
                                                     forest.height,
                                                     forest.texture_varieties,
                                                     transform);

    ref_ptr<Group> group;

    osg::StateSet* stateset = 0;
    StateSetMap::iterator iter = treeTextureMap.find(forest.texture);
    if (iter == treeTextureMap.end()) {
        osg::Texture2D *tex = new osg::Texture2D;
        tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
        tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
        tex->setImage(osgDB::readImageFile(forest.texture));

        static ref_ptr<AlphaFunc> alphaFunc;
        static ref_ptr<Program> program;
        static ref_ptr<Uniform> baseTextureSampler;
        static ref_ptr<Material> material;

        stateset = new osg::StateSet;
        stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
        stateset->setRenderBinDetails(RANDOM_OBJECTS_BIN, "DepthSortedBin");
        if (!program.valid()) {
            alphaFunc = new AlphaFunc;
            alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f);
            program  = new Program;
            baseTextureSampler = new osg::Uniform("baseTexture", 0);
            Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
            program->addShader(vertex_shader);
#ifndef TSG_PACKED_ATTRIBUTES
            program->addBindAttribLocation("textureIndex", 1);
#endif

            Shader* fragment_shader = new Shader(Shader::FRAGMENT,
                                                 fragmentShaderSource);
            program->addShader(fragment_shader);
            material = new Material;
            // Don´t track vertex color
            material->setColorMode(Material::OFF);
            material->setAmbient(Material::FRONT_AND_BACK,
                                 Vec4(.8f, .8f, .8f, 1.0f));
            material->setDiffuse(Material::FRONT_AND_BACK,
                                 Vec4(.2f, .2f, .2f, 1.0f));
        }
        stateset->setAttributeAndModes(alphaFunc.get());
        stateset->setAttribute(program.get());
        stateset->addUniform(baseTextureSampler.get());
        stateset->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
        stateset->setAttribute(material.get());

        treeTextureMap.insert(StateSetMap::value_type(forest.texture,
                                                      stateset));
    } else {
        stateset = iter->second.get();
    }
    // Now, create a quadtree for the forest.
    {
        ShaderGeometryQuadtree quadtree(GetTreeCoord(Matrix::inverse(transform)),
                                        AddTreesLeafObject(),
                                        SG_TREE_QUAD_TREE_DEPTH,
                                        MakeTreesLeaf(forest.range,
                                                      shared_geometry,
                                                      forest.texture_varieties));
        quadtree.buildQuadTree(forest._trees.begin(), forest._trees.end());
        group = quadtree.getRoot();
    }
    group->setStateSet(stateset);
    return group.release();
}

}
<commit_msg>Build trees under a transform note that is rotated to Z-up.<commit_after>/* -*-c++-*-
 *
 * Copyright (C) 2008 Stuart Buchanan
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */

#include <algorithm>
#include <vector>
#include <string>
#include <map>

#include <boost/tuple/tuple_comparison.hpp>

#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Matrix>
#include <osg/StateSet>
#include <osg/Texture2D>
#include <osg/TexEnv>

#include <osgDB/ReadFile>
#include <osgDB/FileUtils>

#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>

#include "ShaderGeometry.hxx"
#include "TreeBin.hxx"

#define SG_TREE_QUAD_TREE_DEPTH 3

// Comments from Tim Moore:
// Some work remains for this code. Stuart's enhancement for multiple
// textures per forest should be integrated. We should try to use one
// ShaderGeometry for *all* the trees in the scene graph and do the
// rotation and scale with a MatrixTransform above the trees quad
// tree. The positions would of course have to be transformed by the
// inverse of that transform. Also, we should investigate whether it
// would be better to instantiate trees as polygons in a osg::Geometry
// object instead of using the ShaderGeometry instancing technique.

using namespace osg;

namespace simgear
{

// memoize geometry
typedef boost::tuple<float, float, int> ForestTuple;
typedef std::map<ForestTuple, ref_ptr<Geometry> > OrthQuadMap;

osg::Geometry* createOrthQuads(float w, float h, int varieties, const osg::Matrix& rotate)
{
    static OrthQuadMap orthQuadMap;
    OrthQuadMap::iterator giter
        = orthQuadMap.find(ForestTuple(w, h, varieties));
    if (giter != orthQuadMap.end())
        return giter->second.get();

    //const osg::Vec3& pos = osg::Vec3(0.0f,0.0f,0.0f),
    // set up the coords
    // Create front and back polygons so we don't need to screw around
    // with two-sided lighting in the shader.
    osg::Vec3Array& v = *(new osg::Vec3Array(8));
    osg::Vec3Array& n = *(new osg::Vec3Array(8));
    osg::Vec2Array& t = *(new osg::Vec2Array(8));

    float cw = w*0.5f;

    v[0].set(0.0f,-cw,0.0f);
    v[1].set(0.0f, cw,0.0f);
    v[2].set(0.0f, cw,h);
    v[3].set(0.0f,-cw,h);

    v[4].set(-cw,0.0f,0.0f);
    v[5].set( cw,0.0f,0.0f);
    v[6].set( cw,0.0f,h);
    v[7].set(-cw,0.0f,h);

    // The texture coordinate range is not the
    // entire coordinate space - as the texture
    // has a number of different trees on it.
    float tx = 1.0f/varieties;

    t[0].set(0.0f,0.0f);
    t[1].set(  tx,0.0f);
    t[2].set(  tx,1.0f);
    t[3].set(0.0f,1.0f);

    t[4].set(0.0f,0.0f);
    t[5].set(  tx,0.0f);
    t[6].set(  tx,1.0f);
    t[7].set(0.0f,1.0f);

    // For now the normal is normal to the quad. If we want to get
    // fancier and approximate a cylindrical tree or something, then
    // we would really want more geometry.
    std::fill(n.begin(), n.begin() + 4, Vec3f(1.0f, 0.0f, 0.0f));
    std::fill(n.begin() + 4, n.end(), Vec3f(0.0f, -1.0f, 0.0f));
    for (unsigned int i = 0; i < 8; i++) {
        v[i] = v[i] * rotate;
        // Should be the inverse transpose, but assume that rotate is
        // orthonormal.
        n[i] = n[i] * rotate;
    }

    osg::Geometry *geom = new osg::Geometry;

    geom->setVertexArray(&v);
    geom->setTexCoordArray(0, &t);
    geom->setNormalArray(&n);
    geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
    // No color for now; that's used to pass the position.
    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,8));

    orthQuadMap.insert(std::make_pair(ForestTuple(w, h, varieties), geom));
    return geom;
}

 static char vertexShaderSource[] =
    "varying float fogFactor;\n"
    "attribute float textureIndex;\n"
    "\n"
    "void main(void)\n"
    "{\n"
    "  gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndex, 0.0, 0.0, 0.0);\n"
    "  vec3 position = gl_Vertex.xyz * gl_Color.w + gl_Color.xyz;\n"
    "  gl_Position   = gl_ModelViewProjectionMatrix * vec4(position,1.0);\n"
    "  vec3 ecPosition = vec3(gl_ModelViewMatrix * vec4(position, 1.0));\n"
    "  vec3 N = normalize(gl_NormalMatrix * gl_Normal);\n"
    "  vec3 diffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(N, gl_LightSource[0].position.xyz));\n"
    "  vec3 backDiffuse = gl_FrontMaterial.diffuse.rgb * max(0.0, dot(-N, gl_LightSource[0].position.xyz));\n"
    " vec4 ambientColor = gl_FrontLightModelProduct.sceneColor + gl_LightSource[0].ambient * gl_FrontMaterial.ambient;\n"
    " gl_FrontColor = ambientColor + gl_LightSource[0].diffuse * vec4(diffuse, 1.0);\n"
    " gl_BackColor = ambientColor + gl_LightSource[0].diffuse * vec4(backDiffuse, 1.0)\n;"
//    "  gl_TexCoord[0] = gl_MultiTexCoord0;\n"
    " float fogCoord = abs(ecPosition.z);\n"
    "  fogFactor = exp( -gl_Fog.density * gl_Fog.density * fogCoord * fogCoord);\n"
    "  fogFactor = clamp(fogFactor, 0.0, 1.0);\n"
    "}\n";

static char fragmentShaderSource[] =
    "uniform sampler2D baseTexture; \n"
    "varying float fogFactor;\n"
    "\n"
    "void main(void) \n"
    "{ \n"
    "  vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n"
    "  vec4 finalColor = base * gl_Color;\n"
    "  gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n"
    "}\n";

typedef std::map<std::string, osg::ref_ptr<StateSet> > StateSetMap;

static StateSetMap treeTextureMap;

// Helper classes for creating the quad tree
namespace
{
struct MakeTreesLeaf
{
    MakeTreesLeaf(float range, Geometry* geometry, int varieties) :
        _range(range), _geometry(geometry), _varieties(varieties)
    {}
    MakeTreesLeaf(const MakeTreesLeaf& rhs) :
        _range(rhs._range), _geometry(rhs._geometry), _varieties(rhs._varieties) {}
    LOD* operator() () const
    {
        LOD* result = new LOD;
        Geode* geode = new Geode;
        ShaderGeometry* sg = new ShaderGeometry(_varieties);
        sg->setGeometry(_geometry);
        geode->addDrawable(sg);
        result->addChild(geode, 0, _range);
        return result;
    }
    float _range;
    int _varieties;
    Geometry* _geometry;
};

struct AddTreesLeafObject
{
    void operator() (LOD* lod, const TreeBin::Tree& tree) const
    {
        Geode* geode = static_cast<Geode*>(lod->getChild(0));
        ShaderGeometry* sg
            = static_cast<ShaderGeometry*>(geode->getDrawable(0));
        sg->addTree(tree);
    }
};

struct GetTreeCoord
{
    Vec3 operator() (const TreeBin::Tree& tree) const
    {
        return tree.position.osg();
    }
};

typedef QuadTreeBuilder<LOD*, TreeBin::Tree, MakeTreesLeaf, AddTreesLeafObject,
                        GetTreeCoord> ShaderGeometryQuadtree;
}

struct TreeTransformer
{
    TreeTransformer(Matrix& mat_) : mat(mat_) {}
    TreeBin::Tree operator()(const TreeBin::Tree& tree) const
    {
        const Vec3& pos = tree.position.osg();
        return TreeBin::Tree(SGVec3f(pos * mat), tree.texture_index,
                             tree.scale);
    }
    Matrix mat;
};

// This actually returns a MatrixTransform node. If we rotate the whole
// forest into the local Z-up coordinate system we can reuse the
// primitive tree geometry for all the forests of the same type.

osg::Group* createForest(TreeBin& forest, const osg::Matrix& transform)
{
    Matrix transInv = Matrix::inverse(transform);
    static Matrix ident;
    // Set up some shared structures.
    osg::Geometry* shared_geometry = createOrthQuads(forest.width,
                                                     forest.height,
                                                     forest.texture_varieties,
                                                     ident);

    ref_ptr<Group> group;

    osg::StateSet* stateset = 0;
    StateSetMap::iterator iter = treeTextureMap.find(forest.texture);
    if (iter == treeTextureMap.end()) {
        osg::Texture2D *tex = new osg::Texture2D;
        tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP );
        tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP );
        tex->setImage(osgDB::readImageFile(forest.texture));

        static ref_ptr<AlphaFunc> alphaFunc;
        static ref_ptr<Program> program;
        static ref_ptr<Uniform> baseTextureSampler;
        static ref_ptr<Material> material;

        stateset = new osg::StateSet;
        stateset->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON );
        stateset->setRenderBinDetails(RANDOM_OBJECTS_BIN, "DepthSortedBin");
        if (!program.valid()) {
            alphaFunc = new AlphaFunc;
            alphaFunc->setFunction(AlphaFunc::GEQUAL,0.33f);
            program  = new Program;
            baseTextureSampler = new osg::Uniform("baseTexture", 0);
            Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource);
            program->addShader(vertex_shader);
            program->addBindAttribLocation("textureIndex", 1);

            Shader* fragment_shader = new Shader(Shader::FRAGMENT,
                                                 fragmentShaderSource);
            program->addShader(fragment_shader);
            material = new Material;
            // Don´t track vertex color
            material->setColorMode(Material::OFF);
            material->setAmbient(Material::FRONT_AND_BACK,
                                 Vec4(.8f, .8f, .8f, 1.0f));
            material->setDiffuse(Material::FRONT_AND_BACK,
                                 Vec4(.2f, .2f, .2f, 1.0f));
        }
        stateset->setAttributeAndModes(alphaFunc.get());
        stateset->setAttribute(program.get());
        stateset->addUniform(baseTextureSampler.get());
        stateset->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON);
        stateset->setAttribute(material.get());

        treeTextureMap.insert(StateSetMap::value_type(forest.texture,
                                                      stateset));
    } else {
        stateset = iter->second.get();
    }
    // Now, create a quadtree for the forest.
    {
        ShaderGeometryQuadtree quadtree(GetTreeCoord(),
                                        AddTreesLeafObject(),
                                        SG_TREE_QUAD_TREE_DEPTH,
                                        MakeTreesLeaf(forest.range,
                                                      shared_geometry,
                                                      forest.texture_varieties));
        // Transform tree positions from the "geocentric" positions we
        // get from the scenery polys into the local Z-up coordinate
        // system.
        std::vector<TreeBin::Tree> rotatedTrees;
        rotatedTrees.reserve(forest._trees.size());
        std::transform(forest._trees.begin(), forest._trees.end(),
                       std::back_inserter(rotatedTrees),
                       TreeTransformer(transInv));
        quadtree.buildQuadTree(rotatedTrees.begin(), rotatedTrees.end());
        group = quadtree.getRoot();
    }
    MatrixTransform* mt = new MatrixTransform(transform);
    for (int i = 0; i < group->getNumChildren(); ++i)
        mt->addChild(group->getChild(i));
    mt->setStateSet(stateset);
    return mt;
}

}
<|endoftext|>


<commit_before>#include "worker.h"
#include "tasks.h"

thread::pool_worker_t::pool_worker_t(tasks_t& queue)
        :       m_queue(queue)
{
}

void thread::pool_worker_t::operator()()
{
        while (true)
        {
                task_t task;

                // wait for a new task to be available in the queue
                {
                        std::unique_lock<std::mutex> lock(m_queue.m_mutex);

                        while ( !m_queue.m_stop &&
                                m_queue.m_tasks.empty())
                        {
                                m_queue.m_condition.wait(lock);
                        }

                        if (m_queue.m_stop)
                        {
                                m_queue.m_running = 0;
                                m_queue.m_tasks.clear();
                                m_queue.m_condition.notify_all();
                                break;
                        }

                        task = m_queue.m_tasks.front();
                        m_queue.m_tasks.pop_front();
                        m_queue.m_running ++;
                }

                // execute the task
                task();

                // announce that a task was completed
                {
                        const std::lock_guard<std::mutex> lock(m_queue.m_mutex);

                        m_queue.m_running --;
                        m_queue.m_condition.notify_all();
                }
        }
}
<commit_msg>simplify implementation of workers<commit_after>#include "worker.h"
#include "tasks.h"

thread::pool_worker_t::pool_worker_t(tasks_t& queue)
        :       m_queue(queue)
{
}

void thread::pool_worker_t::operator()()
{
        while (true)
        {
                task_t task;

                // wait for a new task to be available in the queue
                {
                        std::unique_lock<std::mutex> lock(m_queue.m_mutex);

                        m_queue.m_condition.wait(lock, [&] { return m_queue.m_stop || !m_queue.m_tasks.empty(); });

                        if (m_queue.m_stop)
                        {
                                m_queue.m_running = 0;
                                m_queue.m_tasks.clear();
                                m_queue.m_condition.notify_all();
                                break;
                        }

                        task = m_queue.m_tasks.front();
                        m_queue.m_tasks.pop_front();
                        m_queue.m_running ++;
                }

                // execute the task
                task();

                // announce that a task was completed
                {
                        const std::lock_guard<std::mutex> lock(m_queue.m_mutex);

                        m_queue.m_running --;
                        m_queue.m_condition.notify_all();
                }
        }
}
<|endoftext|>


<commit_before>//===- subzero/src/assembler.cpp - Assembler base class -------------------===//
// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Modified by the Subzero authors.
//
//===----------------------------------------------------------------------===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Assembler class.
//
//===----------------------------------------------------------------------===//

#include "assembler.h"
#include "IceGlobalContext.h"
#include "IceOperand.h"

namespace Ice {

static uintptr_t NewContents(Assembler &assembler, intptr_t capacity) {
  uintptr_t result = assembler.AllocateBytes(capacity);
  return result;
}

AssemblerFixup *AssemblerBuffer::createFixup(FixupKind Kind,
                                             const Constant *Value) {
  AssemblerFixup *F =
      new (assembler_.Allocate<AssemblerFixup>()) AssemblerFixup();
  F->set_position(0);
  F->set_kind(Kind);
  F->set_value(Value);
  fixups_.push_back(F);
  return F;
}

#ifndef NDEBUG
AssemblerBuffer::EnsureCapacity::EnsureCapacity(AssemblerBuffer *buffer) {
  if (buffer->cursor() >= buffer->limit())
    buffer->ExtendCapacity();
  // In debug mode, we save the assembler buffer along with the gap
  // size before we start emitting to the buffer. This allows us to
  // check that any single generated instruction doesn't overflow the
  // limit implied by the minimum gap size.
  buffer_ = buffer;
  gap_ = ComputeGap();
  // Make sure that extending the capacity leaves a big enough gap
  // for any kind of instruction.
  assert(gap_ >= kMinimumGap);
  // Mark the buffer as having ensured the capacity.
  assert(!buffer->HasEnsuredCapacity()); // Cannot nest.
  buffer->has_ensured_capacity_ = true;
}

AssemblerBuffer::EnsureCapacity::~EnsureCapacity() {
  // Unmark the buffer, so we cannot emit after this.
  buffer_->has_ensured_capacity_ = false;
  // Make sure the generated instruction doesn't take up more
  // space than the minimum gap.
  intptr_t delta = gap_ - ComputeGap();
  assert(delta <= kMinimumGap);
}
#endif // !NDEBUG

AssemblerBuffer::AssemblerBuffer(Assembler &assembler) : assembler_(assembler) {
  const intptr_t OneKB = 1024;
  static const intptr_t kInitialBufferCapacity = 4 * OneKB;
  contents_ = NewContents(assembler_, kInitialBufferCapacity);
  cursor_ = contents_;
  limit_ = ComputeLimit(contents_, kInitialBufferCapacity);
#ifndef NDEBUG
  has_ensured_capacity_ = false;
#endif // !NDEBUG

  // Verify internal state.
  assert(Capacity() == kInitialBufferCapacity);
  assert(Size() == 0);
}

AssemblerBuffer::~AssemblerBuffer() {}

void AssemblerBuffer::ExtendCapacity() {
  intptr_t old_size = Size();
  intptr_t old_capacity = Capacity();
  const intptr_t OneMB = 1 << 20;
  intptr_t new_capacity = std::min(old_capacity * 2, old_capacity + OneMB);
  if (new_capacity < old_capacity) {
    llvm::report_fatal_error(
        "Unexpected overflow in AssemblerBuffer::ExtendCapacity");
  }

  // Allocate the new data area and copy contents of the old one to it.
  uintptr_t new_contents = NewContents(assembler_, new_capacity);
  memmove(reinterpret_cast<void *>(new_contents),
          reinterpret_cast<void *>(contents_), old_size);

  // Compute the relocation delta and switch to the new contents area.
  intptr_t delta = new_contents - contents_;
  contents_ = new_contents;

  // Update the cursor and recompute the limit.
  cursor_ += delta;
  limit_ = ComputeLimit(new_contents, new_capacity);

  // Verify internal state.
  assert(Capacity() == new_capacity);
  assert(Size() == old_size);
}

llvm::StringRef Assembler::getBufferView() const {
  return llvm::StringRef(reinterpret_cast<const char *>(buffer_.contents()),
                         buffer_.Size());
}

void Assembler::emitIASBytes(GlobalContext *Ctx) const {
  Ostream &Str = Ctx->getStrEmit();
  intptr_t EndPosition = buffer_.Size();
  intptr_t CurPosition = 0;
  const intptr_t FixupSize = 4;
  for (const AssemblerFixup *NextFixup : fixups()) {
    intptr_t NextFixupLoc = NextFixup->position();
    for (intptr_t i = CurPosition; i < NextFixupLoc; ++i) {
      Str << "\t.byte 0x";
      Str.write_hex(buffer_.Load<uint8_t>(i));
      Str << "\n";
    }
    Str << "\t.long ";
    NextFixup->emit(Ctx);
    if (fixupIsPCRel(NextFixup->kind()))
      Str << " - (. + " << FixupSize << ")";
    Str << "\n";
    CurPosition = NextFixupLoc + FixupSize;
    assert(CurPosition <= EndPosition);
  }
  // Handle any bytes that are not prefixed by a fixup.
  for (intptr_t i = CurPosition; i < EndPosition; ++i) {
    Str << "\t.byte 0x";
    Str.write_hex(buffer_.Load<uint8_t>(i));
    Str << "\n";
  }
}

} // end of namespace Ice
<commit_msg>Add comment for the forked Dart revision for the assembler code.<commit_after>//===- subzero/src/assembler.cpp - Assembler base class -------------------===//
// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Modified by the Subzero authors.
//
// This is forked from Dart revision 39313.
// Please update the revision if we merge back changes from Dart.
// https://code.google.com/p/dart/wiki/GettingTheSource
//
//===----------------------------------------------------------------------===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Assembler class.
//
//===----------------------------------------------------------------------===//

#include "assembler.h"
#include "IceGlobalContext.h"
#include "IceOperand.h"

namespace Ice {

static uintptr_t NewContents(Assembler &assembler, intptr_t capacity) {
  uintptr_t result = assembler.AllocateBytes(capacity);
  return result;
}

AssemblerFixup *AssemblerBuffer::createFixup(FixupKind Kind,
                                             const Constant *Value) {
  AssemblerFixup *F =
      new (assembler_.Allocate<AssemblerFixup>()) AssemblerFixup();
  F->set_position(0);
  F->set_kind(Kind);
  F->set_value(Value);
  fixups_.push_back(F);
  return F;
}

#ifndef NDEBUG
AssemblerBuffer::EnsureCapacity::EnsureCapacity(AssemblerBuffer *buffer) {
  if (buffer->cursor() >= buffer->limit())
    buffer->ExtendCapacity();
  // In debug mode, we save the assembler buffer along with the gap
  // size before we start emitting to the buffer. This allows us to
  // check that any single generated instruction doesn't overflow the
  // limit implied by the minimum gap size.
  buffer_ = buffer;
  gap_ = ComputeGap();
  // Make sure that extending the capacity leaves a big enough gap
  // for any kind of instruction.
  assert(gap_ >= kMinimumGap);
  // Mark the buffer as having ensured the capacity.
  assert(!buffer->HasEnsuredCapacity()); // Cannot nest.
  buffer->has_ensured_capacity_ = true;
}

AssemblerBuffer::EnsureCapacity::~EnsureCapacity() {
  // Unmark the buffer, so we cannot emit after this.
  buffer_->has_ensured_capacity_ = false;
  // Make sure the generated instruction doesn't take up more
  // space than the minimum gap.
  intptr_t delta = gap_ - ComputeGap();
  assert(delta <= kMinimumGap);
}
#endif // !NDEBUG

AssemblerBuffer::AssemblerBuffer(Assembler &assembler) : assembler_(assembler) {
  const intptr_t OneKB = 1024;
  static const intptr_t kInitialBufferCapacity = 4 * OneKB;
  contents_ = NewContents(assembler_, kInitialBufferCapacity);
  cursor_ = contents_;
  limit_ = ComputeLimit(contents_, kInitialBufferCapacity);
#ifndef NDEBUG
  has_ensured_capacity_ = false;
#endif // !NDEBUG

  // Verify internal state.
  assert(Capacity() == kInitialBufferCapacity);
  assert(Size() == 0);
}

AssemblerBuffer::~AssemblerBuffer() {}

void AssemblerBuffer::ExtendCapacity() {
  intptr_t old_size = Size();
  intptr_t old_capacity = Capacity();
  const intptr_t OneMB = 1 << 20;
  intptr_t new_capacity = std::min(old_capacity * 2, old_capacity + OneMB);
  if (new_capacity < old_capacity) {
    llvm::report_fatal_error(
        "Unexpected overflow in AssemblerBuffer::ExtendCapacity");
  }

  // Allocate the new data area and copy contents of the old one to it.
  uintptr_t new_contents = NewContents(assembler_, new_capacity);
  memmove(reinterpret_cast<void *>(new_contents),
          reinterpret_cast<void *>(contents_), old_size);

  // Compute the relocation delta and switch to the new contents area.
  intptr_t delta = new_contents - contents_;
  contents_ = new_contents;

  // Update the cursor and recompute the limit.
  cursor_ += delta;
  limit_ = ComputeLimit(new_contents, new_capacity);

  // Verify internal state.
  assert(Capacity() == new_capacity);
  assert(Size() == old_size);
}

llvm::StringRef Assembler::getBufferView() const {
  return llvm::StringRef(reinterpret_cast<const char *>(buffer_.contents()),
                         buffer_.Size());
}

void Assembler::emitIASBytes(GlobalContext *Ctx) const {
  Ostream &Str = Ctx->getStrEmit();
  intptr_t EndPosition = buffer_.Size();
  intptr_t CurPosition = 0;
  const intptr_t FixupSize = 4;
  for (const AssemblerFixup *NextFixup : fixups()) {
    intptr_t NextFixupLoc = NextFixup->position();
    for (intptr_t i = CurPosition; i < NextFixupLoc; ++i) {
      Str << "\t.byte 0x";
      Str.write_hex(buffer_.Load<uint8_t>(i));
      Str << "\n";
    }
    Str << "\t.long ";
    NextFixup->emit(Ctx);
    if (fixupIsPCRel(NextFixup->kind()))
      Str << " - (. + " << FixupSize << ")";
    Str << "\n";
    CurPosition = NextFixupLoc + FixupSize;
    assert(CurPosition <= EndPosition);
  }
  // Handle any bytes that are not prefixed by a fixup.
  for (intptr_t i = CurPosition; i < EndPosition; ++i) {
    Str << "\t.byte 0x";
    Str.write_hex(buffer_.Load<uint8_t>(i));
    Str << "\n";
  }
}

} // end of namespace Ice
<|endoftext|>