[libcxx-commits] [clang] [libclc] [libcxx] [lld] [llvm] [openmp] llvm encode decode (PR #87187)
Jiang zixian via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Apr 11 00:13:40 PDT 2024
https://github.com/jiang-zixian updated https://github.com/llvm/llvm-project/pull/87187
>From ccf42e2d5e200aabe75e8d328a20c364cdb488cb Mon Sep 17 00:00:00 2001
From: x-codingman <xinhuishao1996 at gmail.com>
Date: Wed, 18 Jan 2023 20:02:45 +0800
Subject: [PATCH 1/8] update Hello.cpp
---
llvm/lib/Transforms/Hello/Hello.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/lib/Transforms/Hello/Hello.cpp b/llvm/lib/Transforms/Hello/Hello.cpp
index b0adb5401f8912..5218d0897000d5 100644
--- a/llvm/lib/Transforms/Hello/Hello.cpp
+++ b/llvm/lib/Transforms/Hello/Hello.cpp
@@ -31,6 +31,7 @@ namespace {
++HelloCounter;
errs() << "Hello: ";
errs().write_escaped(F.getName()) << '\n';
+ F.setSectionPrefix("mySection")
return false;
}
};
>From adfc72708c57e490d0829344982d5d2c5132e0a1 Mon Sep 17 00:00:00 2001
From: xcodingman <xinhuishao1996 at gmail.com>
Date: Fri, 10 Feb 2023 20:59:20 +0800
Subject: [PATCH 2/8] port shadow stack pass
---
lld/ELF/Arch/ARM.cpp | 12 +
llvm/include/llvm/CodeGen/MachineBasicBlock.h | 14 +
llvm/lib/Target/ARM/ARMInstrThumb2.td | 21 +
llvm/lib/Target/ARM/ARMRandezvousCLR.cpp | 335 +++++++
llvm/lib/Target/ARM/ARMRandezvousCLR.h | 54 ++
.../Target/ARM/ARMRandezvousInstrumentor.cpp | 824 +++++++++++++++++
.../Target/ARM/ARMRandezvousInstrumentor.h | 178 ++++
llvm/lib/Target/ARM/ARMRandezvousOptions.cpp | 210 +++++
llvm/lib/Target/ARM/ARMRandezvousOptions.h | 63 ++
.../Target/ARM/ARMRandezvousShadowStack.cpp | 861 ++++++++++++++++++
.../lib/Target/ARM/ARMRandezvousShadowStack.h | 40 +
llvm/lib/Target/ARM/ARMTargetMachine.cpp | 9 +
llvm/lib/Target/ARM/CMakeLists.txt | 9 +
.../Target/ARM/MCTargetDesc/ARMAsmBackend.cpp | 30 +
.../ARM/MCTargetDesc/ARMELFObjectWriter.cpp | 12 +
.../Target/ARM/MCTargetDesc/ARMFixupKinds.h | 3 +-
.../ARM/MCTargetDesc/ARMMCCodeEmitter.cpp | 4 +
llvm/lib/Transforms/Hello/Hello.cpp | 2 +-
18 files changed, 2679 insertions(+), 2 deletions(-)
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousCLR.cpp
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousCLR.h
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousInstrumentor.cpp
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousInstrumentor.h
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousOptions.h
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousShadowStack.cpp
create mode 100644 llvm/lib/Target/ARM/ARMRandezvousShadowStack.h
diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp
index 24d78ebf58203c..49ca1dab20ee8e 100644
--- a/lld/ELF/Arch/ARM.cpp
+++ b/lld/ELF/Arch/ARM.cpp
@@ -848,6 +848,18 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
((lo & 0x7000) >> 4) | // imm3
(lo & 0x00ff)); // imm8
}
+
+ case R_ARM_PRIVATE_14:
+ // Encoding T2: A = imm4:imm12
+ write16le(loc, (read16le(loc) & ~0x000f) | ((val >> 28) & 0x000f));
+ write16le(loc + 2, (read16le(loc + 2) & ~0x0fff) | ((val >> 16) & 0x0fff));
+ break;
+ case R_ARM_PRIVATE_15:
+ // Encoding T2: A = imm4:imm12
+ write16le(loc, (read16le(loc) & ~0x000f) | ((val >> 12) & 0x000f));
+ write16le(loc + 2, (read16le(loc + 2) & ~0x0fff) | (val & 0x0fff));
+ break;
+
case R_ARM_ALU_PC_G0:
case R_ARM_ALU_PC_G0_NC:
case R_ARM_ALU_PC_G1:
diff --git a/llvm/include/llvm/CodeGen/MachineBasicBlock.h b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
index 1ab24b554f5b5e..6e4da3676929f4 100644
--- a/llvm/include/llvm/CodeGen/MachineBasicBlock.h
+++ b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
@@ -185,6 +185,9 @@ class MachineBasicBlock
/// Indicate that this basic block is the indirect dest of an INLINEASM_BR.
bool IsInlineAsmBrIndirectTarget = false;
+ /// Indicate that this basic block is a trap block inserted by ARM Randezvous
+ /// Code Layout Randomization pass.
+ bool IsRandezvousTrapBlock = false;
/// since getSymbol is a relatively heavy-weight operation, the symbol
/// is only computed once and is cached.
mutable MCSymbol *CachedMCSymbol = nullptr;
@@ -667,7 +670,18 @@ class MachineBasicBlock
void setIsInlineAsmBrIndirectTarget(bool V = true) {
IsInlineAsmBrIndirectTarget = V;
}
+
+ /// Returns true if this is a trap block inserted by ARM Randezvous Code
+ /// Layout Randomization pass.
+ bool isRandezvousTrapBlock() const {
+ return IsRandezvousTrapBlock;
+ }
+ /// Indicates if this is a trap block inserted by ARM Randezvous Code Layout
+ /// Randomization pass.
+ void setIsRandezvousTrapBlock(bool V = true) {
+ IsRandezvousTrapBlock = V;
+ }
/// Returns true if it is legal to hoist instructions into this block.
bool isLegalToHoistInto() const;
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td
index d8721cbebd2dc2..248362b108d0c3 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb2.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td
@@ -1506,6 +1506,14 @@ def : T2Pat<(extloadi16 (ARMWrapper tconstpool:$addr)),
// not via pattern.
// Indexed loads
+let isReturn = 1, isTerminator = 1, isBarrier = 1, mayLoad = 1,
+ hasSideEffects = 0, isCodeGenOnly = 1 in
+def t2LDR_PRE_RET : T2Ipreldst<0, 0b10, 1, 1, (outs GPR:$Rt, GPR:$Rn_wb),
+ (ins t2addrmode_imm8_pre:$addr),
+ AddrModeT2_i8, IndexModePre, IIC_iLoad_iu,
+ "ldr", "\t$Rt, $addr!", "$addr.base = $Rn_wb",
+ []>,
+ Sched<[WriteLd]>;
let mayLoad = 1, hasSideEffects = 0 in {
def t2LDR_PRE : T2Ipreldst<0, 0b10, 1, 1, (outs GPR:$Rt, GPR:$Rn_wb),
@@ -2846,6 +2854,19 @@ def t2UDF : T2XI<(outs), (ins imm0_65535:$imm16), IIC_Br, "udf.w\t$imm16",
let Inst{11-0} = imm16{11-0};
}
+let isCodeGenOnly = 1 in
+def t2UDF_ga : T2XI<(outs), (ins imm0_65535_expr:$imm16), IIC_Br,
+ "udf.w\t$imm16", []>, Sched<[WriteBr]> {
+ bits<16> imm16;
+ let Inst{31-29} = 0b111;
+ let Inst{28-27} = 0b10;
+ let Inst{26-20} = 0b1111111;
+ let Inst{19-16} = imm16{15-12};
+ let Inst{15} = 0b1;
+ let Inst{14-12} = 0b010;
+ let Inst{11-0} = imm16{11-0};
+}
+
// A8.6.18 BFI - Bitfield insert (Encoding T1)
let Constraints = "$src = $Rd" in {
def t2BFI : T2TwoRegBitFI<(outs rGPR:$Rd),
diff --git a/llvm/lib/Target/ARM/ARMRandezvousCLR.cpp b/llvm/lib/Target/ARM/ARMRandezvousCLR.cpp
new file mode 100644
index 00000000000000..3beda9b8f9018f
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousCLR.cpp
@@ -0,0 +1,335 @@
+//===- ARMRandezvousCLR.cpp - ARM Randezvous Code Layout Randomization ----===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of a pass that randomizes the code
+// layout of ARM machine code.
+//
+//===----------------------------------------------------------------------===//
+
+#define DEBUG_TYPE "arm-randezvous-clr"
+
+#include "ARMRandezvousCLR.h"
+#include "ARMRandezvousOptions.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/IRBuilder.h"
+
+using namespace llvm;
+
+char ARMRandezvousCLR::ID = 0;
+
+STATISTIC(NumTraps, "Number of trap instructions inserted");
+STATISTIC(NumFuncsBBLR, "Number of functions with basic blocks reordered");
+STATISTIC(NumJumps4BBLR, "Number of jump instructions inserted due to BBLR");
+STATISTIC(NumFuncsBBCLR, "Number of functions with basic block clusters reordered");
+
+ARMRandezvousCLR::ARMRandezvousCLR(bool LateStage)
+ : ModulePass(ID), LateStage(LateStage) {
+}
+
+StringRef
+ARMRandezvousCLR::getPassName() const {
+ return "ARM Randezvous Code Layout Randomization Pass";
+}
+
+void
+ARMRandezvousCLR::getAnalysisUsage(AnalysisUsage & AU) const {
+ // We need this to access MachineFunctions
+ AU.addRequired<MachineModuleInfoWrapperPass>();
+
+ AU.setPreservesCFG();
+ ModulePass::getAnalysisUsage(AU);
+}
+
+//
+// Method: shuffleMachineBasicBlocks()
+//
+// Description:
+// This method shuffles the order of MachineBasicBlocks in a MachineFunction.
+// It shuffles all the basic blocks except the entry block, so fall-through
+// blocks will be taken apart and branch instructions will be inserted
+// appropriately to preserve the CFG.
+//
+// Input:
+// MF - A reference to the MachineFunction.
+//
+// Output:
+// MF - The transformed MachineFunction.
+//
+void
+ARMRandezvousCLR::shuffleMachineBasicBlocks(MachineFunction & MF) {
+ // Shuffling has no effect on functions with fewer than 3 MachineBasicBlocks
+ // (because we are not reordering the entry block)
+ if (MF.size() < 3) {
+ return;
+ }
+
+ // Add an unconditional branch to all MachineBasicBlocks that fall through so
+ // that we can safely take them apart from their fall-through blocks
+ std::vector<MachineBasicBlock *> MBBs;
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+ for (MachineBasicBlock & MBB : MF) {
+ MachineBasicBlock * FallThruMBB = MBB.getFallThrough();
+ if (FallThruMBB != nullptr) {
+ BuildMI(MBB, MBB.end(), DebugLoc(), TII->get(ARM::t2B))
+ .addMBB(FallThruMBB)
+ .add(predOps(ARMCC::AL));
+ ++NumJumps4BBLR;
+ }
+ MBBs.push_back(&MBB);
+ }
+
+ // Now do shuffling; ilist (iplist_impl) does not support iterator
+ // increment/decrement so we have to first do out-of-place shuffling and then
+ // do in-place removal and insertion
+ auto & MBBList = (&MF)->*(MachineFunction::getSublistAccess)(nullptr);
+ llvm::shuffle(MBBs.begin() + 1, MBBs.end(), *RNG);
+ for (MachineBasicBlock * MBB : MBBs) {
+ MBBList.remove(MBB);
+ }
+ for (MachineBasicBlock * MBB : MBBs) {
+ MBBList.push_back(MBB);
+ }
+ ++NumFuncsBBLR;
+}
+
+//
+// Method: shuffleMachineBasicBlockClusters()
+//
+// Description:
+// This method shuffles the order of clusters of MachineBasicBlocks that fall
+// through in the order as they appear. It shuffles all the basic block
+// clusters except the entry cluster.
+//
+// Input:
+// MF - A reference to the MachineFunction.
+//
+// Output:
+// MF - The transformed MachineFunction.
+//
+void
+ARMRandezvousCLR::shuffleMachineBasicBlockClusters(MachineFunction & MF) {
+ auto & MBBList = (&MF)->*(MachineFunction::getSublistAccess)(nullptr);
+
+ // Construct a list of clusters
+ std::vector<std::vector<MachineBasicBlock *> *> Clusters;
+ std::vector<MachineBasicBlock *> * CurrentCluster = nullptr;
+ for (MachineBasicBlock & MBB : MF) {
+ if (CurrentCluster == nullptr) {
+ CurrentCluster = new std::vector<MachineBasicBlock *>();
+ }
+ CurrentCluster->push_back(&MBB);
+ if (!MBB.canFallThrough()) {
+ Clusters.push_back(CurrentCluster);
+ CurrentCluster = nullptr;
+ }
+ }
+
+ do {
+ // Shuffling has no effect on functions with fewer than 3 clusters (because
+ // we are not reordering the entry cluster)
+ if (Clusters.size() < 3) {
+ break;
+ }
+
+ // Now do shuffling; ilist (iplist_impl) does not support iterator
+ // increment/decrement so we have to first do out-of-place shuffling and
+ // then do in-place removal and insertion
+ llvm::shuffle(Clusters.begin() + 1, Clusters.end(), *RNG);
+ for (auto * Cluster : Clusters) {
+ for (MachineBasicBlock * MBB : *Cluster) {
+ MBBList.remove(MBB);
+ }
+ }
+ for (auto * Cluster : Clusters) {
+ for (MachineBasicBlock * MBB : *Cluster) {
+ MBBList.push_back(MBB);
+ }
+ }
+ ++NumFuncsBBCLR;
+ } while (false);
+
+ // Garbage collection
+ for (auto * Cluster : Clusters) {
+ delete Cluster;
+ }
+}
+
+//
+// Method: insertTrapBlocks()
+//
+// Description:
+// This method inserts a given number of trap instructions into a Function
+// and keeps track of each inserted trap instruction as a single basic block.
+//
+// Inputs:
+// F - A reference to the Function.
+// MF - A reference to the MachineFunction to which F corresponds.
+// NumTrapInsts - Total number of trap instructions to insert.
+//
+// Output:
+// MF - The transformed MachineFunction.
+//
+void
+ARMRandezvousCLR::insertTrapBlocks(Function & F, MachineFunction & MF,
+ uint64_t NumTrapInsts) {
+ LLVMContext & Ctx = F.getContext();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+
+ //
+ // In machine IR, disperse trap blocks throughout the MachineFunction, where
+ // the insertion points are the MachineBasicBlocks that do not fall through;
+ // this allows us to preserve the CFG while adding randomness to the inside
+ // of the MachineFunction.
+ //
+ // In LLVM IR, simply place trap blocks at the end of the Function.
+ //
+
+ // Determine where to insert trap instructions
+ std::vector<MachineBasicBlock *> InsertionPts;
+ for (MachineBasicBlock & MBB : MF) {
+ if (!MBB.canFallThrough() && !MBB.isRandezvousTrapBlock()) {
+ InsertionPts.push_back(&MBB);
+ }
+ }
+
+ // Determine the numbers of trap instructions to insert at each point
+ uint64_t SumShares = 0;
+ std::vector<uint64_t> Shares(InsertionPts.size());
+ for (uint64_t i = 0; i < InsertionPts.size(); ++i) {
+ Shares[i] = (*RNG)() & 0xffffffff; // Prevent overflow
+ SumShares += Shares[i];
+ }
+ for (uint64_t i = 0; i < InsertionPts.size(); ++i) {
+ Shares[i] = Shares[i] * NumTrapInsts / SumShares;
+ }
+
+ // Do insertion
+ for (uint64_t i = 0; i < InsertionPts.size(); ++i) {
+ for (uint64_t j = 0; j < Shares[i]; ++j) {
+ // Build an IR basic block
+ BasicBlock * BB = BasicBlock::Create(Ctx, "", &F);
+ IRBuilder<> IRB(BB);
+ IRB.CreateUnreachable();
+
+ // Build a machine IR basic block
+ MachineBasicBlock * MBB = MF.CreateMachineBasicBlock(BB);
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2UDF_ga)).addImm(0);
+ MF.push_back(MBB);
+ MBB->moveAfter(InsertionPts[i]);
+ MBB->setMachineBlockAddressTaken();
+ MBB->setIsRandezvousTrapBlock();
+
+ ++NumTraps;
+ }
+ }
+}
+
+//
+// Method: runOnModule()
+//
+// Description:
+// This method is called when the PassManager wants this pass to transform
+// the specified Module. This method shuffles the order of functions within
+// the module and/or the order of basic blocks within each function, and
+// inserts trap instructions to fill the text section.
+//
+// Input:
+// M - A reference to the Module to transform.
+//
+// Output:
+// M - The transformed Module.
+//
+// Return value:
+// true - The Module was transformed.
+// false - The Module was not transformed.
+//
+bool
+ARMRandezvousCLR::runOnModule(Module & M) {
+ if (!EnableRandezvousCLR) {
+ return false;
+ }
+
+ MachineModuleInfo & MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+ Twine RNGName = getPassName() + "-" + Twine(RandezvousCLRSeed);
+ RNG = M.createRNG(RNGName.str());
+
+ // First, shuffle the order of basic blocks in each function (if requested
+ // and at the late stage) and calculate how much space existing functions
+ // have taken up
+ uint64_t TotalTextSize = 0;
+ std::vector<std::pair<Function *, MachineFunction *> > Functions;
+ for (Function & F : M) {
+ MachineFunction * MF = MMI.getMachineFunction(F);
+ if (MF == nullptr) {
+ continue;
+ }
+
+ if (LateStage) {
+ if (EnableRandezvousBBLR) {
+ shuffleMachineBasicBlocks(*MF);
+ } else if (EnableRandezvousBBCLR) {
+ shuffleMachineBasicBlockClusters(*MF);
+ }
+ }
+
+ uint64_t TextSize = getFunctionCodeSize(*MF);
+ if (TextSize != 0) {
+ Functions.push_back(std::make_pair(&F, MF));
+ TotalTextSize += TextSize;
+ }
+ }
+ assert(TotalTextSize <= RandezvousMaxTextSize && "Text size exceeds the limit");
+
+ if (LateStage) {
+ // Second, shuffle the order of functions; SymbolTableList (iplist_impl)
+ // does not support iterator increment/decrement so we have to first do
+ // out-of-place shuffling and then do in-place removal and insertion
+ SymbolTableList<Function> & FunctionList = M.getFunctionList();
+ llvm::shuffle(Functions.begin(), Functions.end(), *RNG);
+ for (auto & FMF : Functions) {
+ FunctionList.remove(FMF.first);
+ }
+ for (auto & FMF : Functions) {
+ FunctionList.push_back(FMF.first);
+ }
+ }
+
+ // Third, determine the numbers of trap instructions to insert
+ uint64_t NumTrapInsts = (RandezvousMaxTextSize - TotalTextSize) / 4;
+ uint64_t SumShares = 0;
+ std::vector<uint64_t> Shares(Functions.size());
+ if (!LateStage) {
+ // Insert 80% of trap instructions during the early stage; this allows most
+ // of trap blocks to be consumed by later passes while still keeping a
+ // considerable code size budget for later passes and the late-stage CLR
+ // pass
+ NumTrapInsts = NumTrapInsts * 80 / 100;
+ }
+ for (uint64_t i = 0; i < Functions.size(); ++i) {
+ Shares[i] = (*RNG)() & 0xffffffff; // Prevent overflow
+ SumShares += Shares[i];
+ }
+ for (uint64_t i = 0; i < Functions.size(); ++i) {
+ Shares[i] = Shares[i] * NumTrapInsts / SumShares;
+ }
+
+ // Lastly, insert trap instructions into each function
+ for (uint64_t i = 0; i < Functions.size(); ++i) {
+ insertTrapBlocks(*Functions[i].first, *Functions[i].second, Shares[i]);
+ }
+
+ return true;
+}
+
+ModulePass *
+llvm::createARMRandezvousCLR(bool EarlyTrapInsertion) {
+ return new ARMRandezvousCLR(EarlyTrapInsertion);
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousCLR.h b/llvm/lib/Target/ARM/ARMRandezvousCLR.h
new file mode 100644
index 00000000000000..b54e6438c8ff25
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousCLR.h
@@ -0,0 +1,54 @@
+//===- ARMRandezvousCLR.h - ARM Randezvous Code Layout Randomization ------===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the interfaces of a pass that randomizes the code layout
+// of ARM machine code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARM_RANDEZVOUS_CLR
+#define ARM_RANDEZVOUS_CLR
+
+#include "ARMRandezvousInstrumentor.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+
+namespace llvm {
+ struct ARMRandezvousCLR : public ModulePass, ARMRandezvousInstrumentor {
+ // Pass Identifier
+ static char ID;
+
+ ARMRandezvousCLR(bool LateStage);
+ virtual StringRef getPassName() const override;
+ void getAnalysisUsage(AnalysisUsage & AU) const override;
+ virtual bool runOnModule(Module & M) override;
+
+ private:
+ // Which stage we are at:
+ //
+ // * Early stage: insert most of trap instructions for trap block consumers
+ // between early and late stages
+ //
+ // * Late stage: shuffle code layout and insert the rest of trap
+ // instructions
+ bool LateStage = false;
+
+ std::unique_ptr<RandomNumberGenerator> RNG;
+
+ void shuffleMachineBasicBlocks(MachineFunction & MF);
+ void shuffleMachineBasicBlockClusters(MachineFunction & MF);
+ void insertTrapBlocks(Function & F, MachineFunction & MF,
+ uint64_t NumTrapInsts);
+ };
+
+ ModulePass * createARMRandezvousCLR(bool LateStage);
+}
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousInstrumentor.cpp b/llvm/lib/Target/ARM/ARMRandezvousInstrumentor.cpp
new file mode 100644
index 00000000000000..af62fc2de7604b
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousInstrumentor.cpp
@@ -0,0 +1,824 @@
+//===- ARMRandezvousInstrumentor.cpp - A helper class for instrumentation -===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of a class that can help passes of
+// its subclass easily instrument ARM machine IR without concerns of breaking
+// IT blocks.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARMRandezvousInstrumentor.h"
+#include "llvm/CodeGen/LivePhysRegs.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+
+using namespace llvm;
+
+//
+// Method: getITBlockSize()
+//
+// Description:
+// This method computes how many predicated instructions an IT instruction
+// covers.
+//
+// Input:
+// IT - A reference to an IT instruction.
+//
+// Return value:
+// The number of predicated instructions IT covers.
+//
+unsigned
+ARMRandezvousInstrumentor::getITBlockSize(const MachineInstr & IT) {
+ assert(IT.getOpcode() == ARM::t2IT && "Not an IT instruction!");
+
+ unsigned Mask = IT.getOperand(1).getImm() & 0xf;
+ assert(Mask != 0 && "Invalid IT mask!");
+
+ if (Mask & 0x1) {
+ return 4;
+ } else if (Mask & 0x2) {
+ return 3;
+ } else if (Mask & 0x4) {
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+//
+// Method: findIT()
+//
+// Description:
+// This method finds the IT instruction that forms an IT block containing a
+// given instruction MI. It also computes the distance (from 0 to 4, 0 means
+// MI itself is IT) between the IT and MI. If there is no such IT, a null
+// pointer is returned.
+//
+// Input:
+// MI - A reference to an instruction from which to find IT.
+//
+// Output:
+// distance - A reference to an unsigned to store the distance.
+//
+// Return value:
+// A pointer to IT if found, nullptr otherwise.
+//
+MachineInstr *
+ARMRandezvousInstrumentor::findIT(MachineInstr & MI, unsigned & distance) {
+ MachineInstr * Prev = &MI;
+ unsigned dist = 0;
+ while (Prev != nullptr && dist < 5 && Prev->getOpcode() != ARM::t2IT) {
+ // Only count non-meta instructions
+ if (!Prev->isMetaInstruction()) {
+ ++dist;
+ }
+ Prev = Prev->getPrevNode();
+ }
+ if (Prev != nullptr && dist < 5 && Prev->getOpcode() == ARM::t2IT) {
+ if (getITBlockSize(*Prev) >= dist) {
+ distance = dist;
+ return Prev;
+ }
+ }
+ return nullptr;
+}
+
+//
+// Method: findIT()
+//
+// Description:
+// This method finds the IT instruction that forms an IT block containing a
+// given instruction MI. It also computes the distance (from 0 to 4, 0 means
+// MI itself is IT) between the IT and MI. If there is no such IT, a null
+// pointer is returned.
+//
+// Input:
+// MI - A const reference to an instruction from which to find IT.
+//
+// Output:
+// distance - A reference to an unsigned to store the distance.
+//
+// Return value:
+// A const pointer to IT if found, nullptr otherwise.
+//
+const MachineInstr *
+ARMRandezvousInstrumentor::findIT(const MachineInstr & MI, unsigned & distance) {
+ return findIT(const_cast<MachineInstr &>(MI), distance);
+}
+
+//
+// Method: insertInstBefore()
+//
+// Description:
+// This method inserts an instruction Inst before a given instruction MI. If
+// MI is a predicated instruction within an IT block, then Inst will have the
+// same predicate as MI and also end up in an IT block.
+//
+// Inputs:
+// MI - A reference to an instruction before which to insert Inst.
+// Inst - A pointer to an instruction to insert.
+//
+void
+ARMRandezvousInstrumentor::insertInstBefore(MachineInstr & MI,
+ MachineInstr * Inst) {
+ insertInstsBefore(MI, { Inst });
+}
+
+//
+// Method: insertInstAfter()
+//
+// Description:
+// This method inserts an instruction Inst after a given instruction MI. If
+// MI is a predicated instruction within an IT block, then Inst will have the
+// same predicate as MI and also end up in an IT block.
+//
+// Inputs:
+// MI - A reference to an instruction after which to insert Inst.
+// Inst - A pointer to an instruction to insert.
+//
+void
+ARMRandezvousInstrumentor::insertInstAfter(MachineInstr & MI,
+ MachineInstr * Inst) {
+ insertInstsAfter(MI, { Inst });
+}
+
+//
+// Method: insertInstsBefore()
+//
+// Description:
+// This method inserts a group of instructions contained in an array before a
+// given instruction MI. If MI is a predicated instruction within an IT
+// block, then the new instructions will have the same predicate as MI and
+// also end up in one or more IT blocks.
+//
+// Inputs:
+// MI - A reference to an instruction before which to insert instructions.
+// Insts - A reference to an array containing the instructions.
+//
+void
+ARMRandezvousInstrumentor::insertInstsBefore(MachineInstr & MI,
+ ArrayRef<MachineInstr *> Insts) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ MachineFunction & MF = *MI.getMF();
+ MachineBasicBlock & MBB = *MI.getParent();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+
+ unsigned distance;
+ MachineInstr * IT = findIT(MI, distance);
+
+ // Do insert new instructions before MI
+ for (MachineInstr * Inst : Insts) {
+ MBB.insert(MI, Inst);
+ }
+
+ // If MI is inside an IT block, we should make sure to cover all new
+ // instructions with IT(s)
+ if (IT != nullptr && distance != 0) {
+ unsigned ITBlockSize = getITBlockSize(*IT);
+ unsigned Mask = IT->getOperand(1).getImm() & 0xf;
+ ARMCC::CondCodes firstCond = (ARMCC::CondCodes)IT->getOperand(0).getImm();
+ std::deque<bool> DQMask = decodeITMask(Mask);
+ bool sameAsFirstCond = DQMask[distance - 1];
+
+ // Find the range of instructions that are supposed to be in IT block(s)
+ MachineBasicBlock::iterator firstMI(IT->getNextNode()); // Inclusive
+ MachineBasicBlock::iterator lastMI(MI); // Non-inclusive
+ for (unsigned i = distance; i <= ITBlockSize; ) {
+ ++lastMI;
+ // Skip meta instructions if we have not reached the end
+ if (i == ITBlockSize || !lastMI->isMetaInstruction()) {
+ ++i;
+ }
+ }
+
+ // Track new non-meta instructions in DQMask
+ auto it = DQMask.begin();
+ for (unsigned i = 0; i < distance - 1; ++i) {
+ it++;
+ }
+ size_t NumRealInsts = Insts.size();
+ for (MachineInstr * Inst : Insts) {
+ if (Inst->isMetaInstruction()) {
+ --NumRealInsts;
+ }
+ }
+ DQMask.insert(it, NumRealInsts, sameAsFirstCond);
+
+ // Insert ITs to cover instructions in [firstMI, lastMI)
+ for (MachineBasicBlock::iterator i(firstMI); i != lastMI; ) {
+ std::deque<bool> NewDQMask;
+ MachineBasicBlock::iterator j(i);
+ for (unsigned k = 0; k < 4 && j != lastMI; ++j) {
+ if (j->isMetaInstruction()) {
+ continue;
+ }
+ NewDQMask.push_back(DQMask.front());
+ DQMask.pop_front();
+ ++k;
+ }
+ bool flip = false;
+ if (!NewDQMask[0]) {
+ for (unsigned k = 0; k < NewDQMask.size(); ++k) {
+ NewDQMask[k] = !NewDQMask[k];
+ }
+ flip = true;
+ }
+ BuildMI(MBB, i, IT->getDebugLoc(), TII->get(ARM::t2IT))
+ .addImm(flip ? ARMCC::getOppositeCondition(firstCond) : firstCond)
+ .addImm(encodeITMask(NewDQMask));
+ i = j; // Update i here
+ }
+
+ // Remove the original IT
+ IT->eraseFromParent();
+ }
+}
+
+//
+// Method: insertInstsAfter()
+//
+// Description:
+// This method inserts a group of instructions contained in an array after a
+// given instruction MI. If MI is a predicated instruction within an IT
+// block, then the new instructions will have the same predicate as MI and
+// also end up in one or more IT blocks.
+//
+// Inputs:
+// MI - A reference to an instruction after which to insert instructions.
+// Insts - A reference to an array containing the instructions.
+//
+void
+ARMRandezvousInstrumentor::insertInstsAfter(MachineInstr & MI,
+ ArrayRef<MachineInstr *> Insts) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ MachineFunction & MF = *MI.getMF();
+ MachineBasicBlock & MBB = *MI.getParent();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+ MachineBasicBlock::iterator NextMI(MI); ++NextMI;
+
+ unsigned distance;
+ MachineInstr * IT = findIT(MI, distance);
+
+ // Do insert new instructions after MI
+ for (MachineInstr * Inst : Insts) {
+ MBB.insert(NextMI, Inst);
+ }
+
+ // If MI is inside an IT block, we should make sure to cover all new
+ // instructions with IT(s)
+ if (IT != nullptr && distance != 0) {
+ unsigned ITBlockSize = getITBlockSize(*IT);
+ unsigned Mask = IT->getOperand(1).getImm() & 0xf;
+ ARMCC::CondCodes firstCond = (ARMCC::CondCodes)IT->getOperand(0).getImm();
+ std::deque<bool> DQMask = decodeITMask(Mask);
+ bool sameAsFirstCond = DQMask[distance - 1];
+
+ // Find the range of instructions that are supposed to be in IT block(s)
+ MachineBasicBlock::iterator firstMI(IT->getNextNode()); // Inclusive
+ MachineBasicBlock::iterator lastMI(Insts.back()); // Non-inclusive
+ for (unsigned i = distance; i <= ITBlockSize; ) {
+ ++lastMI;
+ // Skip meta instructions if we have not reached the end
+ if (i == ITBlockSize || !lastMI->isMetaInstruction()) {
+ ++i;
+ }
+ }
+
+ // Track new non-meta instructions in DQMask
+ auto it = DQMask.begin();
+ for (unsigned i = 0; i <= distance - 1; ++i) {
+ it++;
+ }
+ size_t NumRealInsts = Insts.size();
+ for (MachineInstr * Inst : Insts) {
+ if (Inst->isMetaInstruction()) {
+ --NumRealInsts;
+ }
+ }
+ DQMask.insert(it, NumRealInsts, sameAsFirstCond);
+
+ // Insert ITs to cover instructions in [firstMI, lastMI)
+ for (MachineBasicBlock::iterator i(firstMI); i != lastMI; ) {
+ std::deque<bool> NewDQMask;
+ MachineBasicBlock::iterator j(i);
+ for (unsigned k = 0; k < 4 && j != lastMI; ++j) {
+ if (j->isMetaInstruction()) {
+ continue;
+ }
+ NewDQMask.push_back(DQMask.front());
+ DQMask.pop_front();
+ ++k;
+ }
+ bool flip = false;
+ if (!NewDQMask[0]) {
+ for (unsigned k = 0; k < NewDQMask.size(); ++k) {
+ NewDQMask[k] = !NewDQMask[k];
+ }
+ flip = true;
+ }
+ BuildMI(MBB, i, IT->getDebugLoc(), TII->get(ARM::t2IT))
+ .addImm(flip ? ARMCC::getOppositeCondition(firstCond) : firstCond)
+ .addImm(encodeITMask(NewDQMask));
+ i = j; // Update i here
+ }
+
+ // Remove the original IT
+ IT->eraseFromParent();
+ }
+}
+
+//
+// Method: removeInst()
+//
+// Description:
+// This method removes a given instruction MI from machine IR. If MI is a
+// predicated instruction within an IT block, then its corresponding IT
+// instruction will be updated or removed as well. Note that MI cannot be an
+// IT instruction itself.
+//
+// Input:
+// MI - A reference to the instruction to remove.
+//
+void
+ARMRandezvousInstrumentor::removeInst(MachineInstr & MI) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ unsigned distance;
+ MachineInstr * IT = findIT(MI, distance);
+
+ // If MI was inside an IT block, we should make sure to update/remove the IT
+ // instruction
+ if (IT != nullptr) {
+ assert(distance != 0 && "Cannot remove an IT instruction directly!");
+
+ unsigned Mask = IT->getOperand(1).getImm() & 0xf;
+ ARMCC::CondCodes firstCond = (ARMCC::CondCodes)IT->getOperand(0).getImm();
+ std::deque<bool> DQMask = decodeITMask(Mask);
+
+ // Remove MI's entry from DQMask
+ auto it = DQMask.begin();
+ for (unsigned i = 0; i < distance - 1; ++i) {
+ it++;
+ }
+ DQMask.erase(it);
+
+ // Remove IT as well if MI was the only instruction in the IT block
+ if (DQMask.empty()) {
+ IT->eraseFromParent();
+ } else {
+ // If MI was the first instruction in the IT block, removing MI might
+ // change the first condition, in which case we need to flip it
+ if (!DQMask[0]) {
+ for (unsigned i = 0; i < DQMask.size(); ++i) {
+ DQMask[i] = !DQMask[i];
+ }
+ IT->getOperand(0).setImm(ARMCC::getOppositeCondition(firstCond));
+ }
+ // Update the IT mask
+ IT->getOperand(1).setImm(encodeITMask(DQMask));
+ }
+ }
+
+ // Now do remove MI
+ MI.eraseFromParent();
+}
+
+//
+// Method: splitBasicBlockBefore()
+//
+// Description:
+// This method splits a basic block into two basic blocks at the point right
+// before a given instruction MI. If MI is the first instruction in the
+// basic block, an empty basic block will be created. If MI is a predicated
+// instruction within an IT block, then a new IT instruction will be inserted
+// at the beginning of the new basic block to cover MI and any succeeding
+// instructions that were in the original IT block. In that case, the old IT
+// instruction will also be updated accordingly.
+//
+// Input:
+// MI - A reference to an instruction before which to split the basic block.
+//
+// Return value:
+// A pointer to the new basic block that contains MI.
+//
+MachineBasicBlock *
+ARMRandezvousInstrumentor::splitBasicBlockBefore(MachineInstr & MI) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ unsigned distance;
+ MachineInstr * IT = findIT(MI, distance);
+
+ MachineFunction & MF = *MI.getMF();
+ MachineBasicBlock & MBB = *MI.getParent();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+
+ // Create a new basic block
+ MachineBasicBlock & NewMBB = *MF.CreateMachineBasicBlock(MBB.getBasicBlock());
+ MF.push_back(&NewMBB);
+ NewMBB.moveAfter(&MBB);
+
+ // Move instructions from the old basic block to the new basic block
+ NewMBB.splice(NewMBB.end(), &MBB, &MI, MBB.end());
+
+ // Move successors from the old basic block to the new basic block
+ NewMBB.transferSuccessors(&MBB);
+ MBB.addSuccessor(&NewMBB);
+
+ // If MI was inside an IT block (but not the IT instruction itself), we
+ // should make sure to update/remove the IT instruction and insert a new IT
+ // in the new basic block
+ if (IT != nullptr && distance > 0) {
+ // Shortcut for just moving the IT over
+ if (distance == 1) {
+ NewMBB.splice(&MI, &MBB, IT);
+ } else {
+ unsigned Mask = IT->getOperand(1).getImm() & 0xf;
+ ARMCC::CondCodes firstCond = (ARMCC::CondCodes)IT->getOperand(0).getImm();
+ std::deque<bool> DQMask = decodeITMask(Mask);
+
+ // Create a new DQMask and adjust both
+ auto it = DQMask.begin();
+ for (unsigned i = 0; i < distance - 1; ++i) {
+ it++;
+ }
+ std::deque<bool> NewDQMask(it, DQMask.end());
+ DQMask.erase(it, DQMask.end());
+
+ // Update the old IT mask
+ IT->getOperand(1).setImm(encodeITMask(DQMask));
+
+ // Create a new IT
+ bool flip = false;
+ if (!NewDQMask[0]) {
+ for (unsigned i = 0; i < NewDQMask.size(); ++i) {
+ NewDQMask[i] = !NewDQMask[i];
+ }
+ flip = true;
+ }
+ BuildMI(NewMBB, MI, IT->getDebugLoc(), TII->get(ARM::t2IT))
+ .addImm(flip ? ARMCC::getOppositeCondition(firstCond) : firstCond)
+ .addImm(encodeITMask(NewDQMask));
+ }
+ }
+
+ return &NewMBB;
+}
+
+//
+// Method: splitBasicBlockAfter()
+//
+// Description:
+// This method splits a basic block into two basic blocks at the point right
+// after a given instruction MI. If MI is the last instruction in the basic
+// block, an empty basic block will be created. If MI is a predicated
+// instruction within an IT block and is not the last one, then a new IT
+// instruction will be inserted at the beginning of the new basic block to
+// cover MI's succeeding instructions that were in the original IT block. In
+// that case, the old IT instruction will also be updated accordingly.
+//
+// Input:
+// MI - A reference to an instruction after which to split the basic block.
+//
+// Return value:
+// A pointer to the new basic block that contains MI's next instruction if
+// the split was done, nullptr otherwise.
+//
+MachineBasicBlock *
+ARMRandezvousInstrumentor::splitBasicBlockAfter(MachineInstr & MI) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ unsigned distance;
+ MachineInstr * IT = findIT(MI, distance);
+
+ MachineFunction & MF = *MI.getMF();
+ MachineBasicBlock & MBB = *MI.getParent();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+
+ // Create a new basic block
+ MachineBasicBlock & NewMBB = *MF.CreateMachineBasicBlock(MBB.getBasicBlock());
+ MF.push_back(&NewMBB);
+ NewMBB.moveAfter(&MBB);
+
+ // Move instructions from the old basic block to the new basic block
+ MachineBasicBlock::iterator next = ++MI.getIterator();
+ NewMBB.splice(NewMBB.end(), &MBB, next, MBB.end());
+
+ // Move successors from the old basic block to the new basic block
+ NewMBB.transferSuccessors(&MBB);
+ MBB.addSuccessor(&NewMBB);
+
+ // If MI was inside an IT block and is not the last one, we should make sure
+ // to update/remove the IT instruction and insert a new IT in the new basic
+ // block
+ if (IT != nullptr && distance < 4) {
+ // Shortcut for just moving the IT over
+ if (distance == 0) {
+ NewMBB.splice(NewMBB.begin(), &MBB, IT);
+ } else {
+ unsigned Mask = IT->getOperand(1).getImm() & 0xf;
+ ARMCC::CondCodes firstCond = (ARMCC::CondCodes)IT->getOperand(0).getImm();
+ std::deque<bool> DQMask = decodeITMask(Mask);
+
+ // Create a new DQMask and adjust both
+ auto it = DQMask.begin();
+ for (unsigned i = 0; i < distance; ++i) {
+ it++;
+ }
+ std::deque<bool> NewDQMask(it, DQMask.end());
+ DQMask.erase(it, DQMask.end());
+
+ // Update the old IT mask
+ IT->getOperand(1).setImm(encodeITMask(DQMask));
+
+ // Create a new IT
+ bool flip = false;
+ if (!NewDQMask[0]) {
+ for (unsigned i = 0; i < NewDQMask.size(); ++i) {
+ NewDQMask[i] = !NewDQMask[i];
+ }
+ flip = true;
+ }
+ BuildMI(NewMBB, NewMBB.begin(), IT->getDebugLoc(), TII->get(ARM::t2IT))
+ .addImm(flip ? ARMCC::getOppositeCondition(firstCond) : firstCond)
+ .addImm(encodeITMask(NewDQMask));
+ }
+ }
+
+ return &NewMBB;
+}
+
+//
+// Method: decodeITMask()
+//
+// Description:
+// This method decodes an IT mask in LLVM's representation and puts a list of
+// boolean values in a deque to return. The boolean values represent whether
+// their corresponding instructions in an IT block have the same predicate as
+// the first one (which indicates that the first boolean value is always
+// true).
+//
+// Input:
+// Mask - The IT mask in LLVM's representation (immediate value of the second
+// operand of a t2IT instruction).
+//
+// Return value:
+// A deque of boolean values (see the above description).
+//
+std::deque<bool>
+ARMRandezvousInstrumentor::decodeITMask(unsigned Mask) {
+ Mask &= 0xf;
+ assert(Mask != 0 && "Invalid IT mask!");
+
+ std::deque<bool> DQMask { true };
+ unsigned size = 4;
+ for (unsigned i = 0x1; i < 0x10; i <<= 1) {
+ if (Mask & i) {
+ break;
+ }
+ --size;
+ }
+ for (unsigned i = 3; i > 4 - size; --i) {
+ DQMask.push_back((Mask & (1 << i)) == 0);
+ }
+
+ return DQMask;
+}
+
+//
+// Method: encodeITMask()
+//
+// Description:
+// This method takes an IT mask in the form of a list of boolean values and
+// encodes it into LLVM's representation. The boolean values represent
+// whether their corresponding instructions in an IT block have the same
+// predicate as the first one (which requires that the first boolean value
+// be always true).
+//
+// Input:
+// DQMask - An IT mask in the form of a list of boolean values.
+//
+// Return value:
+// The IT mask in LLVM's representation (immediate value of the second
+// operand of a t2IT instruction).
+//
+unsigned
+ARMRandezvousInstrumentor::encodeITMask(std::deque<bool> DQMask) {
+ assert(!DQMask.empty() && "Invalid deque representation of an IT mask!");
+ assert(DQMask.size() <= 4 && "Invalid deque representation of an IT mask!");
+ assert(DQMask[0] && "Invalid deque representation of an IT mask!");
+
+ unsigned Mask = 0;
+ for (unsigned i = 1; i < DQMask.size(); ++i) {
+ Mask |= DQMask[i] ? 0 : 1;
+ Mask <<= 1;
+ }
+ Mask |= 1;
+ Mask <<= (4 - DQMask.size());
+
+ return Mask;
+}
+
+//
+// Method: findFreeRegistersBefore()
+//
+// Description:
+// This method computes the liveness of ARM core registers before a given
+// instruction MI and returns a list of free core registers that can be
+// used for instrumentation purposes.
+//
+// Inputs:
+// MI - A reference to the instruction before which to find free
+// registers.
+// Thumb - Whether we are looking for Thumb registers (low registers, i,e,,
+// R0 -- R7) or ARM registers (both low and high registers, i.e.,
+// R0 -- R12 and LR).
+//
+// Return value:
+// A vector of free registers (might be empty, if none is found).
+//
+std::vector<Register>
+ARMRandezvousInstrumentor::findFreeRegistersBefore(const MachineInstr & MI,
+ bool Thumb) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ unsigned distance;
+ const MachineInstr * IT = findIT(MI, distance);
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ const MachineFunction & MF = *MI.getMF();
+ const MachineBasicBlock & MBB = *MI.getParent();
+ const MachineRegisterInfo & MRI = MF.getRegInfo();
+ const TargetRegisterInfo * TRI = MF.getSubtarget().getRegisterInfo();
+ LivePhysRegs UsedRegs(*TRI);
+
+ // First add live-out registers of MBB; these registers are considered live
+ // at the end of MBB
+ UsedRegs.addLiveOuts(MBB);
+
+ // Then move backward step by step to compute live registers before MI
+ MachineBasicBlock::const_iterator MBBI(MI);
+ MachineBasicBlock::const_iterator I = MBB.end();
+ while (I != MBBI) {
+ unsigned distance2;
+ const MachineInstr * IT2 = findIT(*--I, distance2);
+ Register PredReg2;
+ ARMCC::CondCodes Pred2 = getInstrPredicate(*I, PredReg2);
+
+ if (IT2 != nullptr && IT == IT2) {
+ // Skip instructions in the same IT block but with a different predicate
+ if (Pred != Pred2) {
+ continue;
+ }
+
+ // A return in the same IT block with the same predicate can reset live
+ // registers to the callee-saved registers
+ if (I->isReturn()) {
+ UsedRegs.init(*TRI);
+ for (auto CSR = MRI.getCalleeSavedRegs(); CSR && *CSR; ++CSR) {
+ UsedRegs.addReg(*CSR);
+ }
+ }
+ }
+
+ UsedRegs.stepBackward(*I);
+ }
+
+ // Now add registers that are neither reserved nor live to a free list
+ const auto LoGPRs = {
+ ARM::R0, ARM::R1, ARM::R2, ARM::R3, ARM::R4, ARM::R5, ARM::R6, ARM::R7,
+ };
+ const auto HiGPRs = {
+ ARM::R8, ARM::R9, ARM::R10, ARM::R11, ARM::R12, ARM::LR,
+ };
+ std::vector<Register> FreeRegs;
+ for (Register Reg : LoGPRs) {
+ if (!MRI.isReserved(Reg) && !UsedRegs.contains(Reg)) {
+ FreeRegs.push_back(Reg);
+ }
+ }
+ if (!Thumb) {
+ for (Register Reg : HiGPRs) {
+ if (!MRI.isReserved(Reg) && !UsedRegs.contains(Reg)) {
+ FreeRegs.push_back(Reg);
+ }
+ }
+ }
+
+ return FreeRegs;
+}
+
+//
+// Method: findFreeRegistersAfter()
+//
+// Description:
+// This method computes the liveness of ARM core registers after a given
+// instruction MI and returns a list of free core registers that can be
+// used for instrumentation purposes.
+//
+// Inputs:
+// MI - A reference to the instruction after which to find free
+// registers.
+// Thumb - Whether we are looking for Thumb registers (low registers, i,e,,
+// R0 -- R7) or ARM registers (both low and high registers, i.e.,
+// R0 -- R12 and LR).
+//
+// Return value:
+// A vector of free registers (might be empty, if none is found).
+//
+std::vector<Register>
+ARMRandezvousInstrumentor::findFreeRegistersAfter(const MachineInstr & MI,
+ bool Thumb) {
+ assert(!MI.isMetaInstruction() && "Cannot instrument meta instruction!");
+
+ unsigned distance;
+ const MachineInstr * IT = findIT(MI, distance);
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ const MachineFunction & MF = *MI.getMF();
+ const MachineBasicBlock & MBB = *MI.getParent();
+ const MachineRegisterInfo & MRI = MF.getRegInfo();
+ const TargetRegisterInfo * TRI = MF.getSubtarget().getRegisterInfo();
+ LivePhysRegs UsedRegs(*TRI);
+
+ // First add live-out registers of MBB; these registers are considered live
+ // at the end of MBB
+ UsedRegs.addLiveOuts(MBB);
+
+ // If there is a return, add registers used by the return as well; here the
+ // rationale is that, if MI is the return, MI will not be stepped over and
+ // therefore the (potentially live) registers used in MI would not be counted
+ MachineBasicBlock::const_iterator Terminator = MBB.getLastNonDebugInstr();
+ if (Terminator != MBB.end() && Terminator->isReturn()) {
+ UsedRegs.addUses(*Terminator);
+ }
+
+ // Then move backward step by step to compute live registers after MI
+ MachineBasicBlock::const_iterator MBBI(MI);
+ MachineBasicBlock::const_iterator I = MBB.end();
+ while (I != MBBI) {
+ unsigned distance2;
+ const MachineInstr * IT2 = findIT(*--I, distance2);
+ Register PredReg2;
+ ARMCC::CondCodes Pred2 = getInstrPredicate(*I, PredReg2);
+
+ if (IT2 != nullptr && IT == IT2) {
+ // Skip instructions in the same IT block but with a different predicate
+ if (Pred != Pred2) {
+ continue;
+ }
+
+ // A return in the same IT block with the same predicate can reset live
+ // registers to the callee-saved registers
+ if (I->isReturn()) {
+ UsedRegs.init(*TRI);
+ for (auto CSR = MRI.getCalleeSavedRegs(); CSR && *CSR; ++CSR) {
+ UsedRegs.addReg(*CSR);
+ }
+
+ // Add registers used by the return; if MI is the return, MI will not
+ // be stepped over and therefore the (potentially live) registers used
+ // in MI would not be counted
+ UsedRegs.addUses(*I);
+ }
+ }
+
+ if (I != MBBI) {
+ UsedRegs.stepBackward(*I);
+ }
+ }
+
+ // Now add registers that are neither reserved nor live to a free list
+ const auto LoGPRs = {
+ ARM::R0, ARM::R1, ARM::R2, ARM::R3, ARM::R4, ARM::R5, ARM::R6, ARM::R7,
+ };
+ const auto HiGPRs = {
+ ARM::R8, ARM::R9, ARM::R10, ARM::R11, ARM::R12, ARM::LR,
+ };
+ std::vector<Register> FreeRegs;
+ for (Register Reg : LoGPRs) {
+ if (!MRI.isReserved(Reg) && !UsedRegs.contains(Reg)) {
+ FreeRegs.push_back(Reg);
+ }
+ }
+ if (!Thumb) {
+ for (Register Reg : HiGPRs) {
+ if (!MRI.isReserved(Reg) && !UsedRegs.contains(Reg)) {
+ FreeRegs.push_back(Reg);
+ }
+ }
+ }
+
+ return FreeRegs;
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousInstrumentor.h b/llvm/lib/Target/ARM/ARMRandezvousInstrumentor.h
new file mode 100644
index 00000000000000..c84c430e7e3149
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousInstrumentor.h
@@ -0,0 +1,178 @@
+//===- ARMRandezvousInstrumentor.h - A helper class for instrumentation ---===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the interfaces of a class that can help passes of its
+// subclass easily instrument ARM machine IR without concerns of breaking IT
+// blocks.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARM_RANDEZVOUS_INSTRUMENTOR
+#define ARM_RANDEZVOUS_INSTRUMENTOR
+
+#include "ARMBaseInstrInfo.h"
+#include <deque>
+namespace llvm {
+ //====================================================================
+ // Static inline functions.
+ //====================================================================
+
+ static inline size_t getBasicBlockCodeSize(const MachineBasicBlock & MBB) {
+ const MachineFunction & MF = *MBB.getParent();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+
+ size_t CodeSize = 0ul;
+ for (const MachineInstr & MI : MBB) {
+ CodeSize += TII->getInstSizeInBytes(MI);
+ }
+
+ return CodeSize;
+ }
+
+ //
+ // Function: getFunctionCodeSize()
+ //
+ // Description:
+ // This function computes the code size of a machine function.
+ //
+ // Input:
+ // MF - A reference to the target machine function.
+ //
+ // Return value:
+ // The size (in bytes) of the machine function.
+ //
+ static inline size_t getFunctionCodeSize(const MachineFunction & MF) {
+ size_t CodeSize = 0ul;
+ for (const MachineBasicBlock & MBB : MF) {
+ CodeSize += getBasicBlockCodeSize(MBB);
+ }
+
+ return CodeSize;
+ }
+
+ //
+ // Function: containsFunctionPointerType()
+ //
+ // Description:
+ // This function examines a Type to see whether it can explicitly contain one
+ // or more function pointers. Note that this function recurses on aggregate
+ // types.
+ //
+ // Input:
+ // Ty - A pointer to a Type to examine.
+ //
+ // Return value:
+ // true - The Type can contain one or more function pointers.
+ // false - The Type does not contain a function pointer.
+ //
+// static inline bool
+// containsFunctionPointerType(Type * Ty) {
+// // Pointer
+// if (PointerType * PtrTy = dyn_cast<PointerType>(Ty)) {
+// return PtrTy->getParamElementType()->isFunctionTy();
+// }
+
+// // Array
+// if (ArrayType * ArrayTy = dyn_cast<ArrayType>(Ty)) {
+// return containsFunctionPointerType(ArrayTy->getElementType());
+// }
+
+// // Struct
+// if (StructType * StructTy = dyn_cast<StructType>(Ty)) {
+// for (Type * ElementTy : StructTy->elements()) {
+// if (containsFunctionPointerType(ElementTy)) {
+// return true;
+// }
+// }
+// }
+
+// // Other types do not contain function pointers
+// return false;
+// }
+
+ //
+ // Function: createNonZeroInitializerFor()
+ //
+ // Description:
+ // This function creates a non-zero Constant initializer for a give Type,
+ // which is supposed to contain one or more function pointers. Note that
+ // this function recurses on aggregate types.
+ //
+ // Input:
+ // Ty - A pointer to a Type for which to create an initializer.
+ //
+ // Return value:
+ // A pointer to a created Constant.
+ //
+ static inline Constant *
+ createNonZeroInitializerFor(Type * Ty) {
+ // Pointer: this is where we insert non-zero values
+ if (PointerType * PtrTy = dyn_cast<PointerType>(Ty)) {
+ return ConstantExpr::getIntToPtr(
+ ConstantInt::get(Type::getInt32Ty(Ty->getContext()), 1), Ty
+ );
+ }
+
+ // Array
+ if (ArrayType * ArrayTy = dyn_cast<ArrayType>(Ty)) {
+ std::vector<Constant *> InitArray;
+ for (uint64_t i = 0; i < ArrayTy->getNumElements(); ++i) {
+ InitArray.push_back(createNonZeroInitializerFor(ArrayTy->getElementType()));
+ }
+ return ConstantArray::get(ArrayTy, InitArray);
+ }
+
+ // Struct
+ if (StructType * StructTy = dyn_cast<StructType>(Ty)) {
+ std::vector<Constant *> InitArray;
+ for (unsigned i = 0; i < StructTy->getNumElements(); ++i) {
+ InitArray.push_back(createNonZeroInitializerFor(StructTy->getElementType(i)));
+ }
+ return ConstantStruct::get(StructTy, InitArray);
+ }
+
+ // Zeroing out other types are fine
+ return Constant::getNullValue(Ty);
+ }
+
+ //====================================================================
+ // Class ARMRandezvousInstrumentor.
+ //====================================================================
+
+ struct ARMRandezvousInstrumentor {
+ void insertInstBefore(MachineInstr & MI, MachineInstr * Inst);
+
+ void insertInstAfter(MachineInstr & MI, MachineInstr * Inst);
+
+ void insertInstsBefore(MachineInstr & MI, ArrayRef<MachineInstr *> Insts);
+
+ void insertInstsAfter(MachineInstr & MI, ArrayRef<MachineInstr *> Insts);
+
+ void removeInst(MachineInstr & MI);
+
+ MachineBasicBlock * splitBasicBlockBefore(MachineInstr & MI);
+
+ MachineBasicBlock * splitBasicBlockAfter(MachineInstr & MI);
+
+ std::vector<Register> findFreeRegistersBefore(const MachineInstr & MI,
+ bool Thumb = false);
+ std::vector<Register> findFreeRegistersAfter(const MachineInstr & MI,
+ bool Thumb = false);
+
+ private:
+ unsigned getITBlockSize(const MachineInstr & IT);
+ MachineInstr * findIT(MachineInstr & MI, unsigned & distance);
+ const MachineInstr * findIT(const MachineInstr & MI, unsigned & distance);
+ std::deque<bool> decodeITMask(unsigned Mask);
+ unsigned encodeITMask(std::deque<bool> DQMask);
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp b/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
new file mode 100644
index 00000000000000..58dbfca265ad4a
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
@@ -0,0 +1,210 @@
+//===- ARMRandezvousOptions.cpp - ARM Randezvous Command Line Options -----===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the command line options for ARM Randezvous passes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ARMRandezvousOptions.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// Randezvous pass enablers
+//===----------------------------------------------------------------------===//
+
+bool EnableRandezvousCLR;
+static cl::opt<bool, true>
+CLR("arm-randezvous-clr",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Code Layout Randomization"),
+ cl::location(EnableRandezvousCLR),
+ cl::init(false));
+
+bool EnableRandezvousBBLR;
+static cl::opt<bool, true>
+BBLR("arm-randezvous-bblr",
+ cl::Hidden,
+ cl::desc("Enable Basic Block Layout Randomization for ARM Randezvous CLR"),
+ cl::location(EnableRandezvousBBLR),
+ cl::init(false));
+
+bool EnableRandezvousBBCLR;
+static cl::opt<bool, true>
+BBCLR("arm-randezvous-bbclr",
+ cl::Hidden,
+ cl::desc("Enable Basic Block Cluster Layout Randomization for ARM Randezvous CLR"),
+ cl::location(EnableRandezvousBBCLR),
+ cl::init(false));
+
+bool EnableRandezvousPicoXOM;
+static cl::opt<bool, true>
+PicoXOM("arm-randezvous-picoxom",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Execute-Only Memory"),
+ cl::location(EnableRandezvousPicoXOM),
+ cl::init(false));
+
+bool EnableRandezvousGDLR;
+static cl::opt<bool, true>
+GDLR("arm-randezvous-gdlr",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Global Data Layout Randomization"),
+ cl::location(EnableRandezvousGDLR),
+ cl::init(false));
+
+bool EnableRandezvousDecoyPointers;
+static cl::opt<bool, true>
+DecoyPointers("arm-randezvous-dp",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Decoy Pointers"),
+ cl::location(EnableRandezvousDecoyPointers),
+ cl::init(false));
+
+bool EnableRandezvousGlobalGuard;
+static cl::opt<bool, true>
+GlobalGuard("arm-randezvous-global-guard",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvou Global Guard"),
+ cl::location(EnableRandezvousGlobalGuard),
+ cl::init(false));
+
+bool EnableRandezvousShadowStack;
+static cl::opt<bool, true>
+ShadowStack("arm-randezvous-shadow-stack",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Shadow Stack"),
+ cl::location(EnableRandezvousShadowStack),
+ cl::init(false));
+
+bool EnableRandezvousRAN;
+static cl::opt<bool, true>
+RAN("arm-randezvous-ran",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Return Address Nullification"),
+ cl::location(EnableRandezvousRAN),
+ cl::init(false));
+
+bool EnableRandezvousLGPromote;
+static cl::opt<bool, true>
+LGPromote("arm-randezvous-lgp",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Local-to-Global Promotion"),
+ cl::location(EnableRandezvousLGPromote),
+ cl::init(false));
+
+bool EnableRandezvousICallLimiter;
+static cl::opt<bool, true>
+ICallLimiter("arm-randezvous-icall-limiter",
+ cl::Hidden,
+ cl::desc("Enable ARM Randezvous Indirect Call Limiter"),
+ cl::location(EnableRandezvousICallLimiter),
+ cl::init(false));
+
+//===----------------------------------------------------------------------===//
+// Randezvous pass seeds
+//===----------------------------------------------------------------------===//
+
+uint64_t RandezvousCLRSeed;
+static cl::opt<uint64_t, true>
+CLRSeed("arm-randezvous-clr-seed",
+ cl::Hidden,
+ cl::desc("Seed for the RNG used in ARM Randezvous CLR"),
+ cl::location(RandezvousCLRSeed),
+ cl::init(0));
+
+uint64_t RandezvousGDLRSeed;
+static cl::opt<uint64_t, true>
+GDLRSeed("arm-randezvous-gdlr-seed",
+ cl::Hidden,
+ cl::desc("Seed for the RNG used in ARM Randezvous GDLR"),
+ cl::location(RandezvousGDLRSeed),
+ cl::init(0));
+
+uint64_t RandezvousShadowStackSeed;
+static cl::opt<uint64_t, true>
+ShadowStackSeed("arm-randezvous-shadow-stack-seed",
+ cl::Hidden,
+ cl::desc("Seed for the RNG used in ARM Randezvous Shadow Stack"),
+ cl::location(RandezvousShadowStackSeed),
+ cl::init(0));
+
+//===----------------------------------------------------------------------===//
+// Size options used by Randezvous passes
+//===----------------------------------------------------------------------===//
+
+size_t RandezvousMaxTextSize;
+static cl::opt<size_t, true>
+MaxTextSize("arm-randezvous-max-text-size",
+ cl::Hidden,
+ cl::desc("Maximum text section size in bytes"),
+ cl::location(RandezvousMaxTextSize),
+ cl::init(0x1e0000)); // 2 MB - 128 KB
+
+size_t RandezvousMaxRodataSize;
+static cl::opt<size_t, true>
+MaxRodataSize("arm-randezvous-max-rodata-size",
+ cl::Hidden,
+ cl::desc("Maximum rodata section size in bytes"),
+ cl::location(RandezvousMaxRodataSize),
+ cl::init(0x10000)); // 64 KB
+
+size_t RandezvousMaxDataSize;
+static cl::opt<size_t, true>
+MaxDataSize("arm-randezvous-max-data-size",
+ cl::Hidden,
+ cl::desc("Maximum data section size in bytes"),
+ cl::location(RandezvousMaxDataSize),
+ cl::init(0x10000)); // 64 KB
+
+size_t RandezvousMaxBssSize;
+static cl::opt<size_t, true>
+MaxBssSize("arm-randezvous-max-bss-size",
+ cl::Hidden,
+ cl::desc("Maximum bss section size in bytes"),
+ cl::location(RandezvousMaxBssSize),
+ cl::init(0x10000)); // 64 KB
+
+size_t RandezvousShadowStackSize;
+static cl::opt<size_t, true>
+ShadowStackSize("arm-randezvous-shadow-stack-size",
+ cl::Hidden,
+ cl::desc("ARM Randezvous Shadow Stack size in bytes"),
+ cl::location(RandezvousShadowStackSize),
+ cl::init(0x8000)); // 32 KB
+
+//===----------------------------------------------------------------------===//
+// Miscellaneous options used by Randezvous passes
+//===----------------------------------------------------------------------===//
+
+unsigned RandezvousShadowStackStrideLength;
+static cl::opt<unsigned, true>
+ShadowStackStrideLength("arm-randezvous-shadow-stack-stride-length",
+ cl::Hidden,
+ cl::desc("Number of bits for ARM Randezvous Shadow Stack stride"),
+ cl::location(RandezvousShadowStackStrideLength),
+ cl::init(8));
+
+unsigned RandezvousNumGlobalGuardCandidates;
+static cl::opt<unsigned, true>
+NumGlobalGuardCandidates("arm-randezvous-num-global-guard-candidates",
+ cl::Hidden,
+ cl::desc("Number of global guard candidates to generate"),
+ cl::location(RandezvousNumGlobalGuardCandidates),
+ cl::init(64));
+
+uintptr_t RandezvousRNGAddress;
+static cl::opt<uintptr_t, true>
+RNGAddress("arm-randezvous-rng-addr",
+ cl::Hidden,
+ cl::desc("Address of a dynamic RNG"),
+ cl::location(RandezvousRNGAddress),
+ cl::init(0));
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousOptions.h b/llvm/lib/Target/ARM/ARMRandezvousOptions.h
new file mode 100644
index 00000000000000..3757fd09e07f42
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousOptions.h
@@ -0,0 +1,63 @@
+//===- ARMRandezvousOptions.h - ARM Randezvous Command Line Options -------===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the command line options for ARM Randezvous passes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ARM_RANDEZVOUS_OPTIONS
+#define ARM_RANDEZVOUS_OPTIONS
+
+#include <cstddef>
+#include <cstdint>
+
+//===----------------------------------------------------------------------===//
+// Randezvous pass enablers
+//===----------------------------------------------------------------------===//
+
+extern bool EnableRandezvousCLR;
+extern bool EnableRandezvousBBLR;
+extern bool EnableRandezvousBBCLR;
+extern bool EnableRandezvousPicoXOM;
+extern bool EnableRandezvousGDLR;
+extern bool EnableRandezvousDecoyPointers;
+extern bool EnableRandezvousGlobalGuard;
+extern bool EnableRandezvousShadowStack;
+extern bool EnableRandezvousRAN;
+extern bool EnableRandezvousLGPromote;
+extern bool EnableRandezvousICallLimiter;
+
+//===----------------------------------------------------------------------===//
+// Randezvous pass seeds
+//===----------------------------------------------------------------------===//
+
+extern uint64_t RandezvousCLRSeed;
+extern uint64_t RandezvousGDLRSeed;
+extern uint64_t RandezvousShadowStackSeed;
+
+//===----------------------------------------------------------------------===//
+// Size options used by Randezvous passes
+//===----------------------------------------------------------------------===//
+
+extern size_t RandezvousMaxTextSize;
+extern size_t RandezvousMaxRodataSize;
+extern size_t RandezvousMaxDataSize;
+extern size_t RandezvousMaxBssSize;
+extern size_t RandezvousShadowStackSize;
+
+//===----------------------------------------------------------------------===//
+// Miscellaneous options used by Randezvous passes
+//===----------------------------------------------------------------------===//
+
+extern unsigned RandezvousShadowStackStrideLength;
+extern unsigned RandezvousNumGlobalGuardCandidates;
+extern uintptr_t RandezvousRNGAddress;
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousShadowStack.cpp b/llvm/lib/Target/ARM/ARMRandezvousShadowStack.cpp
new file mode 100644
index 00000000000000..e0593fabe65e4d
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousShadowStack.cpp
@@ -0,0 +1,861 @@
+//===- ARMRandezvousShadowStack.cpp - ARM Randezvous Shadow Stack ---------===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of a pass that instruments ARM machine
+// code to save/load the return address to/from a randomized compact shadow
+// stack.
+//
+//===----------------------------------------------------------------------===//
+
+#define DEBUG_TYPE "arm-randezvous-shadow-stack"
+
+#include "ARMRandezvousCLR.h"
+#include "ARMRandezvousOptions.h"
+#include "ARMRandezvousShadowStack.h"
+#include "MCTargetDesc/ARMAddressingModes.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+using namespace llvm;
+
+STATISTIC(NumPrologues, "Number of prologues transformed to use shadow stack");
+STATISTIC(NumEpilogues, "Number of epilogues transformed to use shadow stack");
+STATISTIC(NumNullified, "Number of return addresses nullified");
+
+char ARMRandezvousShadowStack::ID = 0;
+
+ARMRandezvousShadowStack::ARMRandezvousShadowStack() : ModulePass(ID) {
+}
+
+StringRef
+ARMRandezvousShadowStack::getPassName() const {
+ return "ARM Randezvous Shadow Stack Pass";
+}
+
+void
+ARMRandezvousShadowStack::getAnalysisUsage(AnalysisUsage & AU) const {
+ // We need this to access MachineFunctions
+ AU.addRequired<MachineModuleInfoWrapperPass>();
+
+ AU.setPreservesCFG();
+ ModulePass::getAnalysisUsage(AU);
+}
+
+void
+ARMRandezvousShadowStack::releaseMemory() {
+ TrapBlocks.clear();
+}
+
+//
+// Method: createShadowStack()
+//
+// Description:
+// This method creates a GlobalVariable as the shadow stack. The shadow
+// stack is initialized either as zeroed memory or with addresses of randomly
+// picked trap blocks.
+//
+// Input:
+// M - A reference to the Module in which to create the shadow stack.
+//
+// Return value:
+// A pointer to the created GlobalVariable.
+//
+GlobalVariable *
+ARMRandezvousShadowStack::createShadowStack(Module & M) {
+ // Create types for the shadow stack
+ uint64_t PtrSize = M.getDataLayout().getPointerSize();
+ LLVMContext & Ctx = M.getContext();
+ PointerType * RetAddrTy = PointerType::getUnqual(Type::getInt8Ty(Ctx));
+ ArrayType * SSTy = ArrayType::get(RetAddrTy,
+ RandezvousShadowStackSize / PtrSize);
+
+ // Create the shadow stack
+ Constant * CSS = M.getOrInsertGlobal(ShadowStackName, SSTy);
+ GlobalVariable * SS = dyn_cast<GlobalVariable>(CSS);
+ assert(SS != nullptr && "Shadow stack has wrong type!");
+ SS->setLinkage(GlobalVariable::LinkOnceAnyLinkage);
+
+ // Initialize the shadow stack if not initialized
+ if (!SS->hasInitializer()) {
+ Constant * SSInit = nullptr;
+ if (EnableRandezvousDecoyPointers) {
+ // Initialize the shadow stack with an array of random values; they are
+ // either random trap block addresses or purely random values with the
+ // LSB set
+ std::vector<Constant *> SSInitArray;
+ for (unsigned i = 0; i < SSTy->getNumElements(); ++i) {
+ if (!TrapBlocks.empty()) {
+ uint64_t Idx = (*RNG)() % TrapBlocks.size();
+ const BasicBlock * BB = TrapBlocks[Idx]->getBasicBlock();
+ SSInitArray.push_back(BlockAddress::get(const_cast<BasicBlock *>(BB)));
+ } else {
+ APInt A(8 * PtrSize, (*RNG)() | 0x1);
+ SSInitArray.push_back(Constant::getIntegerValue(RetAddrTy, A));
+ }
+ }
+ SSInit = ConstantArray::get(SSTy, SSInitArray);
+ } else {
+ // Initialize the shadow stack with zeros
+ SSInit = ConstantArray::getNullValue(SSTy);
+ }
+ SS->setInitializer(SSInit);
+ }
+
+ // Add the shadow stack to @llvm.used
+ appendToUsed(M, { SS });
+
+ return SS;
+}
+
+//
+// Method: createInitFunction()
+//
+// Description:
+// This method creates a function (both Function and MachineFunction) that
+// initializes the reserved registers for the shadow stack.
+//
+// Inputs:
+// M - A reference to the Module in which to create the function.
+// SS - A reference to the shadow stack global variable.
+//
+// Return value:
+// A pointer to the created Function.
+//
+Function *
+ARMRandezvousShadowStack::createInitFunction(Module & M, GlobalVariable & SS) {
+ // Create types for the init function
+ LLVMContext & Ctx = M.getContext();
+ FunctionType * FuncTy = FunctionType::get(Type::getVoidTy(Ctx), false);
+
+ // Create the init function
+ FunctionCallee FC = M.getOrInsertFunction(InitFuncName, FuncTy);
+ Function * F = dyn_cast<Function>(FC.getCallee());
+ assert(F != nullptr && "Init function has wrong type!");
+ MachineModuleInfo & MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+ MachineFunction & MF = MMI.getOrCreateMachineFunction(*F);
+
+ // Set necessary attributes and properties
+ F->setLinkage(GlobalVariable::LinkOnceAnyLinkage);
+ if (!F->hasFnAttribute(Attribute::Naked)) {
+ F->addFnAttr(Attribute::Naked);
+ }
+ if (!F->hasFnAttribute(Attribute::NoUnwind)) {
+ F->addFnAttr(Attribute::NoUnwind);
+ }
+ if (!F->hasFnAttribute(Attribute::WillReturn)) {
+ F->addFnAttr(Attribute::WillReturn);
+ }
+ using Property = MachineFunctionProperties::Property;
+ if (!MF.getProperties().hasProperty(Property::NoVRegs)) {
+ MF.getProperties().set(Property::NoVRegs);
+ }
+
+ // Create a basic block if not created
+ if (F->empty()) {
+ assert(MF.empty() && "Machine IR basic block already there!");
+
+ // Build an IR basic block
+ BasicBlock * BB = BasicBlock::Create(Ctx, "", F);
+ IRBuilder<> IRB(BB);
+ IRB.CreateRetVoid(); // At this point, what the IR basic block contains
+ // doesn't matter so just place a return there
+
+ // Build machine IR basic block(s)
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+ MachineBasicBlock * MBB = MF.CreateMachineBasicBlock(BB);
+ MachineBasicBlock * MBB2 = nullptr;
+ MachineBasicBlock * MBB3 = nullptr;
+ MachineBasicBlock * RetMBB = MBB;
+ MF.push_back(MBB);
+ // MOVi16 SSPtrReg, @SS_lo
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi16), ShadowStackPtrReg)
+ .addGlobalAddress(&SS, 0, ARMII::MO_LO16)
+ .add(predOps(ARMCC::AL));
+ // MOVTi16 SSPtrReg, @SS_hi
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVTi16), ShadowStackPtrReg)
+ .addReg(ShadowStackPtrReg)
+ .addGlobalAddress(&SS, 0, ARMII::MO_HI16)
+ .add(predOps(ARMCC::AL));
+ if (RandezvousRNGAddress != 0) {
+ // User provided an RNG address, so load a random stride from the RNG
+ if (ARM_AM::getT2SOImmVal(RandezvousRNGAddress) != -1) {
+ // Use MOVi if the address can be encoded in Thumb modified constant
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi), ARM::R0)
+ .addImm(RandezvousRNGAddress)
+ .add(predOps(ARMCC::AL))
+ .add(condCodeOp()); // No 'S' bit
+ } else {
+ // Otherwise use MOVi16/MOVTi16 to encode lower/upper 16 bits of the
+ // address
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi16), ARM::R0)
+ .addImm(RandezvousRNGAddress & 0xffff)
+ .add(predOps(ARMCC::AL));
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVTi16), ARM::R0)
+ .addReg(ARM::R0)
+ .addImm((RandezvousRNGAddress >> 16) & 0xffff)
+ .add(predOps(ARMCC::AL));
+ }
+
+ MBB2 = MF.CreateMachineBasicBlock(BB);
+ MF.push_back(MBB2);
+ MBB->addSuccessor(MBB2);
+ MBB2->addSuccessor(MBB2);
+ // LDRi12 SSStrideReg, [R0, #0]
+ BuildMI(MBB2, DebugLoc(), TII->get(ARM::t2LDRi12), ShadowStackStrideReg)
+ .addReg(ARM::R0)
+ .addImm(0)
+ .add(predOps(ARMCC::AL));
+ // CMPi8 SSStrideReg, #0
+ BuildMI(MBB2, DebugLoc(), TII->get(ARM::t2CMPri))
+ .addReg(ShadowStackStrideReg)
+ .addImm(0)
+ .add(predOps(ARMCC::AL));
+ // BEQ MBB2
+ BuildMI(MBB2, DebugLoc(), TII->get(ARM::t2Bcc))
+ .addMBB(MBB2)
+ .addImm(ARMCC::EQ)
+ .addReg(ARM::CPSR, RegState::Kill);
+
+ MBB3 = MF.CreateMachineBasicBlock(BB);
+ MF.push_back(MBB3);
+ MBB2->addSuccessor(MBB3);
+ // BFC SSStrideReg, #(SSStrideLength - 1), #(33 - SSStrideLength)
+ BuildMI(MBB3, DebugLoc(), TII->get(ARM::t2BFC), ShadowStackStrideReg)
+ .addReg(ShadowStackStrideReg)
+ .addImm((1 << (RandezvousShadowStackStrideLength - 1)) - 1)
+ .add(predOps(ARMCC::AL));
+ // BFC SSStrideReg, #0, #2
+ BuildMI(MBB3, DebugLoc(), TII->get(ARM::t2BFC), ShadowStackStrideReg)
+ .addReg(ShadowStackStrideReg)
+ .addImm(~0x3)
+ .add(predOps(ARMCC::AL));
+ RetMBB = MBB3;
+ } else {
+ // Generate a static random stride
+ uint64_t Stride = (*RNG)();
+ Stride &= (1ul << (RandezvousShadowStackStrideLength - 1)) - 1;
+ Stride &= ~0x3ul;
+ if (ARM_AM::getT2SOImmVal(Stride) != -1) {
+ // Use MOVi if the stride can be encoded in Thumb modified constant
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi), ShadowStackStrideReg)
+ .addImm(Stride)
+ .add(predOps(ARMCC::AL))
+ .add(condCodeOp()); // No 'S' bit
+ } else {
+ // Otherwise use MOVi16/MOVTi16 to encode lower/upper 16 bits of the
+ // stride
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi16), ShadowStackStrideReg)
+ .addImm(Stride & 0xffff)
+ .add(predOps(ARMCC::AL));
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVTi16), ShadowStackStrideReg)
+ .addReg(ShadowStackStrideReg)
+ .addImm((Stride >> 16) & 0xffff)
+ .add(predOps(ARMCC::AL));
+ }
+ }
+ // BX_RET
+ BuildMI(RetMBB, DebugLoc(), TII->get(ARM::tBX_RET))
+ .add(predOps(ARMCC::AL));
+ }
+
+ // Add the init function to @llvm.used
+ appendToUsed(M, { F });
+
+ return F;
+}
+
+//
+// Method: pushToShadowStack()
+//
+// Description:
+// This method modifies a PUSH instruction to not save LR to the stack and
+// inserts new instructions that save LR to the shadow stack.
+//
+// Inputs:
+// MI - A reference to a PUSH instruction that saves LR to the stack.
+// LR - A reference to the LR operand of the PUSH.
+// Stride - A static stride to use.
+//
+// Return value:
+// true - The machine code was modified.
+//
+bool
+ARMRandezvousShadowStack::pushToShadowStack(MachineInstr & MI,
+ MachineOperand & LR,
+ uint32_t Stride) {
+ MachineFunction & MF = *MI.getMF();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc & DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ // Build the following instruction sequence
+ //
+ // STR_POST LR, [SSPtrReg], #Stride
+ // ADDrr SSPtrReg, SSPtrReg, SSStrideReg
+ std::vector<MachineInstr *> NewInsts;
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2STR_POST), ShadowStackPtrReg)
+ .addReg(ARM::LR)
+ .addReg(ShadowStackPtrReg)
+ .addImm(Stride)
+ .add(predOps(Pred, PredReg)));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2ADDrr), ShadowStackPtrReg)
+ .addReg(ShadowStackPtrReg)
+ .addReg(ShadowStackStrideReg)
+ .add(predOps(Pred, PredReg))
+ .add(condCodeOp()));
+
+ // Now insert these new instructions into the basic block
+ insertInstsBefore(MI, NewInsts);
+
+ // At last, replace the old PUSH with a new one that doesn't push LR to the
+ // stack
+ switch (MI.getOpcode()) {
+ case ARM::t2STMDB_UPD:
+ // STMDB_UPD should store at least two registers; if it happens to be two,
+ // we replace it with a STR_PRE
+ assert(MI.getNumExplicitOperands() >= 6 && "Buggy STMDB_UPD!");
+ if (MI.getNumExplicitOperands() > 6) {
+ MI.removeOperand(MI.getOperandNo(&LR));
+ } else {
+ unsigned Idx = MI.getOperandNo(&LR);
+ Idx = Idx == 4 ? 5 : 4;
+ insertInstBefore(MI, BuildMI(MF, DL, TII->get(ARM::t2STR_PRE), ARM::SP)
+ .add(MI.getOperand(Idx))
+ .addReg(ARM::SP)
+ .addImm(-4)
+ .add(predOps(Pred, PredReg))
+ .setMIFlags(MI.getFlags()));
+ removeInst(MI);
+ }
+ break;
+
+ case ARM::tPUSH:
+ // PUSH should store at least one register; if it happens to be one, we
+ // just remove it
+ assert(MI.getNumExplicitOperands() >= 3 && "Buggy PUSH!");
+ if (MI.getNumExplicitOperands() > 3) {
+ MI.removeOperand(MI.getOperandNo(&LR));
+ } else {
+ removeInst(MI);
+ }
+ break;
+
+ // ARM::t2STR_PRE
+ default:
+ // STR_PRE only stores one register, so we just remove it
+ removeInst(MI);
+ break;
+ }
+
+ ++NumPrologues;
+ return true;
+}
+
+//
+// Method: popFromShadowStack()
+//
+// Description:
+// This method modifies a POP instruction to not write to PC/LR and inserts
+// new instructions that load the return address from the shadow stack into
+// PC/LR.
+//
+// Inputs:
+// MI - A reference to a POP instruction that writes to LR or PC.
+// PCLR - A reference to the PC or LR operand of the POP.
+// Stride - A static stride to use.
+//
+// Return value:
+// true - The machine code was modified.
+//
+bool
+ARMRandezvousShadowStack::popFromShadowStack(MachineInstr & MI,
+ MachineOperand & PCLR,
+ uint32_t Stride) {
+ MachineFunction & MF = *MI.getMF();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc & DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ // Build the following instruction sequence
+ //
+ // SUBrr SSPtrReg, SSPtrReg, SSStrideReg
+ // LDR_PRE PC/LR, [SSPtrReg, #-Stride]!
+ std::vector<MachineInstr *> NewInsts;
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2SUBrr), ShadowStackPtrReg)
+ .addReg(ShadowStackPtrReg)
+ .addReg(ShadowStackStrideReg)
+ .add(predOps(Pred, PredReg))
+ .add(condCodeOp()));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(PCLR.getReg() == ARM::PC ?
+ ARM::t2LDR_PRE_RET :
+ ARM::t2LDR_PRE),
+ PCLR.getReg())
+ .addReg(ShadowStackPtrReg, RegState::Define)
+ .addReg(ShadowStackPtrReg)
+ .addImm(-Stride)
+ .add(predOps(Pred, PredReg)));
+
+ // Now insert these new instructions into the basic block
+ insertInstsAfter(MI, NewInsts);
+
+ // Replace the old POP with a new one that doesn't write to PC/LR
+ switch (MI.getOpcode()) {
+ case ARM::t2LDMIA_RET:
+ MI.setDesc(TII->get(ARM::t2LDMIA_UPD));
+ NewInsts[1]->copyImplicitOps(MF, MI);
+ for (unsigned i = MI.getNumOperands() - 1, e = MI.getNumExplicitOperands();
+ i >= e; --i) {
+ MI.removeOperand(i);
+ }
+ LLVM_FALLTHROUGH;
+ case ARM::t2LDMIA_UPD:
+ // LDMIA_UPD should load at least two registers; if it happens to be two,
+ // we replace it with a LDR_POST
+ assert(MI.getNumExplicitOperands() >= 6 && "Buggy LDMIA_UPD!");
+ if (MI.getNumExplicitOperands() > 6) {
+ MI.removeOperand(MI.getOperandNo(&PCLR));
+ } else {
+ unsigned Idx = MI.getOperandNo(&PCLR);
+ Idx = Idx == 4 ? 5 : 4;
+ insertInstAfter(MI, BuildMI(MF, DL, TII->get(ARM::t2LDR_POST),
+ MI.getOperand(Idx).getReg())
+ .addReg(ARM::SP, RegState::Define)
+ .addReg(ARM::SP)
+ .addImm(4)
+ .add(predOps(Pred, PredReg))
+ .setMIFlags(MI.getFlags()));
+ removeInst(MI);
+ }
+ break;
+
+ case ARM::tPOP_RET:
+ MI.setDesc(TII->get(ARM::tPOP));
+ NewInsts[1]->copyImplicitOps(MF, MI);
+ for (unsigned i = MI.getNumOperands() - 1, e = MI.getNumExplicitOperands();
+ i >= e; --i) {
+ MI.removeOperand(i);
+ }
+ LLVM_FALLTHROUGH;
+ case ARM::tPOP:
+ // POP should load at least one register; if it happens to be one, we just
+ // remove it
+ assert(MI.getNumExplicitOperands() >= 3 && "Buggy POP!");
+ if (MI.getNumExplicitOperands() > 3) {
+ MI.removeOperand(MI.getOperandNo(&PCLR));
+ } else {
+ removeInst(MI);
+ }
+ break;
+
+ // ARM::t2LDR_POST
+ default:
+ // LDR_POST only loads one register, so we just remove it
+ removeInst(MI);
+ break;
+ }
+
+ if (EnableRandezvousRAN) {
+ // Nullify the return address in the shadow stack
+ nullifyReturnAddress(*NewInsts[1], NewInsts[1]->getOperand(0));
+ }
+
+ ++NumEpilogues;
+ return true;
+}
+
+//
+// Method: nullifyReturnAddress()
+//
+// Description:
+// This method nullifies an in-memory return address by either zeroing it out
+// or filling it with a null value (either the address of a randomly picked
+// trap block or a purely random value).
+//
+// Inputs:
+// MI - A reference to a POP or LDR instruction that writes to LR or PC.
+// PCLR - A reference to the PC or LR operand of MI.
+//
+// Return value:
+// true - The machine code was modified.
+//
+bool
+ARMRandezvousShadowStack::nullifyReturnAddress(MachineInstr & MI,
+ MachineOperand & PCLR) {
+ MachineFunction & MF = *MI.getMF();
+ const TargetInstrInfo * TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc & DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ // Mark LR as restored since we're going to use LR to hold the return address
+ // in all the cases
+ MachineFrameInfo & MFI = MF.getFrameInfo();
+ if (MFI.isCalleeSavedInfoValid()) {
+ for (CalleeSavedInfo & CSI : MFI.getCalleeSavedInfo()) {
+ if (CSI.getReg() == ARM::LR) {
+ CSI.setRestored(true);
+ break;
+ }
+ }
+ }
+
+ // We need to use a scratch register as the source register of a store. If
+ // no free register is around, spill and use R4.
+ std::vector<Register> FreeRegs = findFreeRegistersAfter(MI);
+ bool Spill = FreeRegs.empty();
+ Register FreeReg = Spill ? ARM::R4 : FreeRegs[0];
+
+ std::vector<MachineInstr *> NewInsts;
+ switch (MI.getOpcode()) {
+ // LDMIA_RET SP!, {..., PC} -> LDMIA_UPD SP!, {..., LR}
+ // MOVi16 FreeReg, #0
+ // STRi8 FreeReg, [SP, #-4]
+ // BX_RET
+ case ARM::t2LDMIA_RET:
+ assert(PCLR.getReg() == ARM::PC && "Buggy POP!");
+ MI.setDesc(TII->get(ARM::t2LDMIA_UPD));
+ PCLR.setReg(ARM::LR);
+ insertInstAfter(MI, BuildMI(MF, DL, TII->get(ARM::tBX_RET))
+ .add(predOps(Pred, PredReg)));
+ LLVM_FALLTHROUGH;
+ // LDMIA_UPD SP!, {..., LR} -> LDMIA_UPD SP!, {..., LR}
+ // MOVi16 FreeReg, #0
+ // STRi8 FreeReg, [SP, #-4]
+ case ARM::t2LDMIA_UPD:
+ // LDR_POST LR, [SP], #4 -> LDR_POST LR, [SP], #4
+ // MOVi16 FreeReg, #0
+ // STRi8 FreeReg, [SP, #-4]
+ case ARM::t2LDR_POST:
+ assert(PCLR.getReg() == ARM::LR && "Buggy POP!");
+ if (Spill) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPUSH))
+ .add(predOps(Pred, PredReg))
+ .addReg(FreeReg));
+ }
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVi16), FreeReg)
+ .addImm(0)
+ .add(predOps(Pred, PredReg)));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2STRi8))
+ .addReg(FreeReg)
+ .addReg(ARM::SP)
+ .addImm(-4)
+ .add(predOps(Pred, PredReg)));
+ if (Spill) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPOP))
+ .add(predOps(Pred, PredReg))
+ .addReg(FreeReg));
+ }
+ insertInstsAfter(MI, NewInsts);
+ break;
+
+ // POP(_RET) {..., PC} -> LDMIA_UPD SP!, {..., LR}
+ // MOVi16 FreeReg, #0
+ // STRi8 FreeReg, [SP, #-4]
+ // BX_RET
+ case ARM::tPOP:
+ case ARM::tPOP_RET: {
+ assert(PCLR.getReg() == ARM::PC && "Buggy POP!");
+ MachineInstrBuilder MIB = BuildMI(MF, DL, TII->get(ARM::t2LDMIA_UPD), ARM::SP)
+ .addReg(ARM::SP);
+ for (MachineOperand & MO : MI.explicit_operands()) {
+ if (MO.isReg() && MO.getReg() == ARM::PC) {
+ MIB.addReg(ARM::LR, RegState::Define);
+ } else {
+ MIB.add(MO);
+ }
+ }
+ NewInsts.push_back(MIB);
+ if (Spill) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPUSH))
+ .add(predOps(Pred, PredReg))
+ .addReg(FreeReg));
+ }
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVi16), FreeReg)
+ .addImm(0)
+ .add(predOps(Pred, PredReg)));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2STRi8))
+ .addReg(FreeReg)
+ .addReg(ARM::SP)
+ .addImm(-4)
+ .add(predOps(Pred, PredReg)));
+ if (Spill) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPOP))
+ .add(predOps(Pred, PredReg))
+ .addReg(FreeReg));
+ }
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tBX_RET))
+ .add(predOps(Pred, PredReg)));
+ insertInstsAfter(MI, NewInsts);
+ removeInst(MI);
+ break;
+ }
+
+ // LDR_PRE_RET PC, [SSPtrReg, #imm]! -> LDR_PRE LR, [SSPtrReg, #imm]!
+ // MOVi16 FreeReg, #0
+ // STRi12 FreeReg, [SSPtrReg, #0]
+ // BX_RET
+ //
+ // or LDR_PRE LR, [SSPtrReg, #imm]!
+ // MOVi16 FreeReg, #null-lo16
+ // MOVTi16 FreeReg, #null-hi16
+ // STRi12 FreeReg, [SSPtrReg, #0]
+ // BX_RET
+ case ARM::t2LDR_PRE_RET:
+ assert(PCLR.getReg() == ARM::PC && "Buggy POP!");
+ MI.setDesc(TII->get(ARM::t2LDR_PRE));
+ PCLR.setReg(ARM::LR);
+ insertInstAfter(MI, BuildMI(MF, DL, TII->get(ARM::tBX_RET))
+ .add(predOps(Pred, PredReg)));
+ LLVM_FALLTHROUGH;
+ // LDR_PRE LR, [SSPtrReg, #imm]! -> LDR_PRE LR, [SSPtrReg, #imm]!
+ // MOVi16 FreeReg, #0
+ // STRi12 FreeReg, [SSPtrReg, #0]
+ //
+ // or LDR_PRE LR, [SSPtrReg, #imm]!
+ // MOVi16 FreeReg, #null-lo16
+ // MOVTi16 FreeReg, #null-hi16
+ // STRi12 FreeReg, [SSPtrReg, #0]
+ default:
+ assert(MI.getOpcode() == ARM::t2LDR_PRE && "Unrecognized POP!");
+ assert(MI.getOperand(1).getReg() == ShadowStackPtrReg && "Buggy POP!");
+ assert(PCLR.getReg() == ARM::LR && "Buggy POP!");
+ if (Spill) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPUSH))
+ .add(predOps(Pred, PredReg))
+ .addReg(FreeReg));
+ }
+ if (EnableRandezvousDecoyPointers) {
+ if (!TrapBlocks.empty()) {
+ // Use the address of a trap block as the null value
+ uint64_t Idx = (*RNG)() % TrapBlocks.size();
+ const BasicBlock * BB = TrapBlocks[Idx]->getBasicBlock();
+ BlockAddress * BA = BlockAddress::get(const_cast<BasicBlock *>(BB));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVi16), FreeReg)
+ .addBlockAddress(BA, 0, ARMII::MO_LO16)
+ .add(predOps(Pred, PredReg)));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVTi16), FreeReg)
+ .addReg(FreeReg)
+ .addBlockAddress(BA, 0, ARMII::MO_HI16)
+ .add(predOps(Pred, PredReg)));
+ } else {
+ // Use a random value with the LSB set as the null value
+ uint32_t NullValue = (*RNG)() | 0x1;
+ if (ARM_AM::getT2SOImmVal(NullValue) != -1) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVi), FreeReg)
+ .addImm(NullValue)
+ .add(predOps(Pred, PredReg))
+ .add(condCodeOp())); // No 'S' bit
+ } else {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVi16), FreeReg)
+ .addImm(NullValue & 0xffff)
+ .add(predOps(Pred, PredReg)));
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVTi16), FreeReg)
+ .addReg(FreeReg)
+ .addImm((NullValue >> 16) & 0xffff)
+ .add(predOps(Pred, PredReg)));
+ }
+ }
+ } else {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2MOVi16), FreeReg)
+ .addImm(0)
+ .add(predOps(Pred, PredReg)));
+ }
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2STRi12))
+ .addReg(FreeReg)
+ .addReg(ShadowStackPtrReg)
+ .addImm(0)
+ .add(predOps(Pred, PredReg)));
+ if (Spill) {
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPOP))
+ .add(predOps(Pred, PredReg))
+ .addReg(FreeReg));
+ }
+ insertInstsAfter(MI, NewInsts);
+ break;
+ }
+
+ ++NumNullified;
+ return true;
+}
+
+//
+// Method: runOnModule()
+//
+// Description:
+// This method is called when the PassManager wants this pass to transform
+// the specified Module. This method
+//
+// * creates a global variable as the shadow stack,
+//
+// * creates a function that initializes the reserved registers for the
+// shadow stack, and
+//
+// * transforms the Module to utilize the shadow stack for saving/restoring
+// return addresses and/or to nullify a saved return address on returns.
+//
+// Input:
+// M - A reference to the Module to transform.
+//
+// Output:
+// M - The transformed Module.
+//
+// Return value:
+// true - The Module was transformed.
+// false - The Module was not transformed.
+//
+bool
+ARMRandezvousShadowStack::runOnModule(Module & M) {
+ if (!EnableRandezvousShadowStack && !EnableRandezvousRAN) {
+ return false;
+ }
+
+ MachineModuleInfo & MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+ Twine RNGName = getPassName() + "-" + Twine(RandezvousShadowStackSeed);
+ RNG = M.createRNG(RNGName.str());
+
+ // Find trap blocks inserted by CLR
+ for (Function & F : M) {
+ MachineFunction * MF = MMI.getMachineFunction(F);
+ if (MF != nullptr) {
+ for (MachineBasicBlock & MBB : *MF) {
+ if (MBB.isRandezvousTrapBlock()) {
+ TrapBlocks.push_back(&MBB);
+ }
+ }
+ }
+ }
+
+ if (EnableRandezvousShadowStack) {
+ assert((RandezvousShadowStackStrideLength > 2 &&
+ RandezvousShadowStackStrideLength <= 32) && "Invalid stride length!");
+
+ // Create and initialize a global variable for the shadow stack
+ GlobalVariable * SS = createShadowStack(M);
+
+ // Create an init function that:
+ // * loads the address of the shadow stack to the shadow stack pointer
+ // register, and
+ // * generates a random stride (either dynamic or static) to the shadow
+ // stack stride register
+ createInitFunction(M, *SS);
+ }
+
+ // Instrument pushes and pops in each function
+ bool changed = false;
+ for (Function & F : M) {
+ MachineFunction * MF = MMI.getMachineFunction(F);
+ if (MF == nullptr) {
+ continue;
+ }
+
+ // Find out all pushes that write LR to the stack and all pops that read a
+ // return address from the stack to LR or PC
+ std::vector<std::pair<MachineInstr *, MachineOperand *> > Pushes;
+ std::vector<std::pair<MachineInstr *, MachineOperand *> > Pops;
+ for (MachineBasicBlock & MBB : *MF) {
+ for (MachineInstr & MI : MBB) {
+ switch (MI.getOpcode()) {
+ // Frame-setup instructions in function prologue
+ case ARM::t2STR_PRE:
+ case ARM::t2STMDB_UPD:
+ // STR_PRE and STMDB_UPD are considered as PUSH if they write to SP!
+ if (MI.getOperand(0).getReg() != ARM::SP) {
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case ARM::tPUSH:
+ if (MI.getFlag(MachineInstr::FrameSetup)) {
+ for (MachineOperand & MO : MI.explicit_operands()) {
+ if (MO.isReg() && MO.getReg() == ARM::LR) {
+ Pushes.push_back(std::make_pair(&MI, &MO));
+ break;
+ }
+ }
+ }
+ break;
+
+ // Frame-destroy instructions in function epilogue
+ case ARM::t2LDR_POST:
+ case ARM::t2LDMIA_UPD:
+ case ARM::t2LDMIA_RET:
+ // LDR_POST and LDMIA_(UPD|RET) are considered as POP if they read
+ // from SP!
+ if (MI.getOperand(1).getReg() != ARM::SP) {
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case ARM::tPOP:
+ case ARM::tPOP_RET:
+ if (MI.getFlag(MachineInstr::FrameDestroy)) {
+ // Handle 2 cases:
+ // (1) Pop writing to LR
+ // (2) Pop writing to PC
+ for (MachineOperand & MO : MI.explicit_operands()) {
+ if (MO.isReg()) {
+ if (MO.getReg() == ARM::LR || MO.getReg() == ARM::PC) {
+ Pops.push_back(std::make_pair(&MI, &MO));
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // Instrument each push and pop
+ if (EnableRandezvousShadowStack) {
+ // Generate a per-function static stride
+ uint32_t Stride = (*RNG)();
+ Stride &= (1ul << (RandezvousShadowStackStrideLength - 1)) - 1;
+ Stride &= ~0x3ul;
+ // Limit the static stride to be within 8 bits, so that it can fit in
+ // STR_POST and LDR_PRE as an immediate
+ Stride &= 0xfful;
+ // Don't generate an empty stride; either the dynamic stride or the
+ // static stride needs to make sure of it, so just do it on the static to
+ // leave more room for the dynamic
+ if (Stride == 0u) {
+ Stride = 4u;
+ }
+
+ for (auto & MIMO : Pushes) {
+ changed |= pushToShadowStack(*MIMO.first, *MIMO.second, Stride);
+ }
+ for (auto & MIMO : Pops) {
+ changed |= popFromShadowStack(*MIMO.first, *MIMO.second, Stride);
+ }
+ } else if (EnableRandezvousRAN) {
+ for (auto & MIMO : Pops) {
+ changed |= nullifyReturnAddress(*MIMO.first, *MIMO.second);
+ }
+ }
+ }
+
+ return changed;
+}
+
+ModulePass *
+llvm::createARMRandezvousShadowStack(void) {
+ return new ARMRandezvousShadowStack();
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousShadowStack.h b/llvm/lib/Target/ARM/ARMRandezvousShadowStack.h
new file mode 100644
index 00000000000000..5b9fcd61105b50
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMRandezvousShadowStack.h
@@ -0,0 +1,40 @@
+#ifndef ARM_RANDEZVOUS_SHADOW_STACK
+#define ARM_RANDEZVOUS_SHADOW_STACK
+
+#include "ARMRandezvousInstrumentor.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+
+namespace llvm {
+ struct ARMRandezvousShadowStack : public ModulePass, ARMRandezvousInstrumentor {
+ // Pass Identifier
+ static char ID;
+
+ static constexpr Register ShadowStackPtrReg = ARM::R8;
+ static constexpr Register ShadowStackStrideReg = ARM::R9;
+ static constexpr StringRef ShadowStackName = "__randezvous_shadow_stack";
+ static constexpr StringRef InitFuncName = "__randezvous_shadow_stack_init";
+
+ ARMRandezvousShadowStack();
+ virtual StringRef getPassName() const override;
+ void getAnalysisUsage(AnalysisUsage & AU) const override;
+ void releaseMemory() override;
+ virtual bool runOnModule(Module & M) override;
+
+ private:
+ std::unique_ptr<RandomNumberGenerator> RNG;
+ std::vector<MachineBasicBlock *> TrapBlocks;
+
+ GlobalVariable * createShadowStack(Module & M);
+ Function * createInitFunction(Module & M, GlobalVariable & SS);
+ bool pushToShadowStack(MachineInstr & MI, MachineOperand & LR,
+ uint32_t Stride);
+ bool popFromShadowStack(MachineInstr & MI, MachineOperand & PCLR,
+ uint32_t Stride);
+ bool nullifyReturnAddress(MachineInstr & MI, MachineOperand & PCLR);
+ };
+
+ ModulePass * createARMRandezvousShadowStack(void);
+}
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index 775d098fbaed57..bfd0d0ec4cecda 100644
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -9,6 +9,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ARMRandezvousInstrumentor.h"
+#include "ARMRandezvousShadowStack.h"
#include "ARMTargetMachine.h"
#include "ARM.h"
#include "ARMMachineFunctionInfo.h"
@@ -610,6 +612,13 @@ void ARMPassConfig::addPreEmitPass2() {
// sizes will only be decreased by this pass.
addPass(createARMLowOverheadLoopsPass());
+ // Inserts sxh pass
+
+ // Add Randezvous CodeGen passes
+
+ addPass(createARMRandezvousShadowStack());
+
+
if (TM->getTargetTriple().isOSWindows()) {
// Identify valid longjmp targets for Windows Control Flow Guard.
addPass(createCFGuardLongjmpPass());
diff --git a/llvm/lib/Target/ARM/CMakeLists.txt b/llvm/lib/Target/ARM/CMakeLists.txt
index a0f4290cb48db2..d4dd215b84ab83 100644
--- a/llvm/lib/Target/ARM/CMakeLists.txt
+++ b/llvm/lib/Target/ARM/CMakeLists.txt
@@ -19,6 +19,14 @@ tablegen(LLVM ARMGenSystemRegister.inc -gen-searchable-tables)
add_public_tablegen_target(ARMCommonTableGen)
+
+set(ARMRandezvous_SOURCES
+ ARMRandezvousInstrumentor.cpp
+ ARMRandezvousShadowStack.cpp
+ ARMRandezvousOptions.cpp
+ ARMRandezvousCLR.cpp
+)
+
add_llvm_target(ARMCodeGen
A15SDOptimizer.cpp
ARMAsmPrinter.cpp
@@ -68,6 +76,7 @@ add_llvm_target(ARMCodeGen
Thumb2ITBlockPass.cpp
Thumb2InstrInfo.cpp
Thumb2SizeReduction.cpp
+ ${ARMRandezvous_SOURCES}
LINK_COMPONENTS
ARMDesc
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
index dfa305a3b968d7..1a2cab20ee07c3 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
@@ -115,6 +115,10 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
{"fixup_arm_movw_lo16", 0, 20, 0},
{"fixup_t2_movt_hi16", 0, 20, 0},
{"fixup_t2_movw_lo16", 0, 20, 0},
+
+ {"fixup_t2_udf_hi16", 0, 20, 0},
+ {"fixup_t2_udf_lo16", 0, 20, 0},
+
{"fixup_arm_mod_imm", 0, 12, 0},
{"fixup_t2_so_imm", 0, 26, 0},
{"fixup_bf_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel},
@@ -169,6 +173,10 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
{"fixup_arm_movw_lo16", 12, 20, 0},
{"fixup_t2_movt_hi16", 12, 20, 0},
{"fixup_t2_movw_lo16", 12, 20, 0},
+
+ {"fixup_t2_udf_hi16", 12, 20, 0},
+ {"fixup_t2_udf_lo16", 12, 20, 0},
+
{"fixup_arm_mod_imm", 20, 12, 0},
{"fixup_t2_so_imm", 26, 6, 0},
{"fixup_bf_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel},
@@ -443,6 +451,7 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
A->getSymbol().isExternal() &&
(Kind == FK_Data_4 || Kind == ARM::fixup_arm_movw_lo16 ||
Kind == ARM::fixup_arm_movt_hi16 || Kind == ARM::fixup_t2_movw_lo16 ||
+ Kind == ARM::fixup_t2_movt_hi16 || Kind == ARM::fixup_t2_udf_lo16 ||
Kind == ARM::fixup_t2_movt_hi16))
Value |= 1;
}
@@ -472,6 +481,19 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
Value = (Hi4 << 16) | (Lo12);
return Value;
}
+ case ARM::fixup_t2_udf_hi16:
+ assert(STI != nullptr);
+ if (IsResolved || !STI->getTargetTriple().isOSBinFormatELF())
+ Value >>= 16;
+ LLVM_FALLTHROUGH;
+ case ARM::fixup_t2_udf_lo16: {
+ unsigned Hi4 = (Value & 0xF000) >> 12;
+ unsigned Lo12 = Value & 0x0FFF;
+ // inst{19-16} = Hi4;
+ // inst{11-0} = Lo12;
+ Value = (Hi4 << 16) | (Lo12);
+ return swapHalfWords(Value, Endian == support::little);
+ }
case ARM::fixup_t2_movt_hi16:
assert(STI != nullptr);
if (IsResolved || !STI->getTargetTriple().isOSBinFormatELF())
@@ -967,6 +989,10 @@ static unsigned getFixupKindNumBytes(unsigned Kind) {
case ARM::fixup_arm_movw_lo16:
case ARM::fixup_t2_movt_hi16:
case ARM::fixup_t2_movw_lo16:
+
+ case ARM::fixup_t2_udf_hi16:
+ case ARM::fixup_t2_udf_lo16:
+
case ARM::fixup_t2_so_imm:
case ARM::fixup_bf_branch:
case ARM::fixup_bf_target:
@@ -1028,6 +1054,10 @@ static unsigned getFixupKindContainerSizeBytes(unsigned Kind) {
case ARM::fixup_arm_movw_lo16:
case ARM::fixup_t2_movt_hi16:
case ARM::fixup_t2_movw_lo16:
+
+ case ARM::fixup_t2_udf_hi16:
+ case ARM::fixup_t2_udf_lo16:
+
case ARM::fixup_arm_mod_imm:
case ARM::fixup_t2_so_imm:
case ARM::fixup_bf_branch:
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
index df8f54d14a8690..b424bb8e74b603 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
@@ -155,6 +155,11 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
return ELF::R_ARM_THM_BF12;
case ARM::fixup_bfl_target:
return ELF::R_ARM_THM_BF18;
+
+ case ARM::fixup_t2_udf_hi16:
+ return ELF::R_ARM_PRIVATE_14;
+ case ARM::fixup_t2_udf_lo16:
+ return ELF::R_ARM_PRIVATE_15;
}
}
switch (Kind) {
@@ -233,6 +238,8 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
case MCSymbolRefExpr::VK_ARM_SBREL:
return ELF::R_ARM_MOVT_BREL;
}
+
+
case ARM::fixup_arm_movw_lo16:
switch (Modifier) {
default:
@@ -265,6 +272,11 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
case MCSymbolRefExpr::VK_ARM_SBREL:
return ELF::R_ARM_THM_MOVW_BREL_NC;
}
+
+ case ARM::fixup_t2_udf_hi16:
+ return ELF::R_ARM_PRIVATE_14;
+ case ARM::fixup_t2_udf_lo16:
+ return ELF::R_ARM_PRIVATE_15;
}
}
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h b/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h
index 53258a88c7fa27..d59b8f6fd5fefd 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h
@@ -98,7 +98,8 @@ enum Fixups {
fixup_arm_movw_lo16, // :lower16:
fixup_t2_movt_hi16, // :upper16:
fixup_t2_movw_lo16, // :lower16:
-
+ fixup_t2_udf_hi16, // :upper16:
+ fixup_t2_udf_lo16, // :lower16:
// Fixup for mod_imm
fixup_arm_mod_imm,
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp
index bafc0f85375605..7f194e345d5eae 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp
@@ -1230,6 +1230,10 @@ ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
case ARMMCExpr::VK_ARM_LO16:
Kind = MCFixupKind(isThumb(STI) ? ARM::fixup_t2_movw_lo16
: ARM::fixup_arm_movw_lo16);
+
+ if (MI.getOpcode() == ARM::t2UDF_ga) {
+ Kind = MCFixupKind(ARM::fixup_t2_udf_lo16);
+ }
break;
}
diff --git a/llvm/lib/Transforms/Hello/Hello.cpp b/llvm/lib/Transforms/Hello/Hello.cpp
index 5218d0897000d5..92aa1e449f53ab 100644
--- a/llvm/lib/Transforms/Hello/Hello.cpp
+++ b/llvm/lib/Transforms/Hello/Hello.cpp
@@ -30,8 +30,8 @@ namespace {
bool runOnFunction(Function &F) override {
++HelloCounter;
errs() << "Hello: ";
+ F.setSectionPrefix("mySection");
errs().write_escaped(F.getName()) << '\n';
- F.setSectionPrefix("mySection")
return false;
}
};
>From 081cf99bbfa9125f764fc4ee53174b2ead3d4119 Mon Sep 17 00:00:00 2001
From: jiang-zixian <980485445 at qq.com>
Date: Sun, 31 Mar 2024 14:49:19 +0800
Subject: [PATCH 3/8] encode and decode is over!
---
.../Driver/Inputs/CUDA-symlinks/usr/bin/ptxas | 1 -
.../usr/bin/i386-unknown-linux-gnu-ld | 2 +-
.../usr/bin/x86_64-unknown-linux-gnu-ld | 2 +-
.../usr/i386-unknown-linux-gnu/bin/ld | 2 +-
.../usr/x86_64-unknown-linux-gnu/bin/ld | 2 +-
.../multilib_32bit_linux_tree/usr/bin/as | 2 +-
.../multilib_32bit_linux_tree/usr/bin/ld | 2 +-
.../usr/i386-unknown-linux/bin/as | 2 +-
.../usr/i386-unknown-linux/bin/ld | 2 +-
.../multilib_64bit_linux_tree/usr/bin/as | 2 +-
.../multilib_64bit_linux_tree/usr/bin/ld | 2 +-
.../usr/x86_64-unknown-linux/bin/as | 2 +-
.../usr/x86_64-unknown-linux/bin/ld | 2 +-
libclc/amdgcn-mesa3d | 1 -
libclc/amdgcn-mesa3d/lib/SOURCES | 3 +
.../lib/workitem/get_global_size.cl | 23 +
.../lib/workitem/get_local_size.cl | 29 +
.../lib/workitem/get_num_groups.cl | 12 +
libclc/clspv64 | 1 -
libclc/clspv64/lib/SOURCES | 48 ++
libclc/clspv64/lib/math/fma.cl | 256 +++++++
libclc/clspv64/lib/math/nextafter.cl | 5 +
libclc/clspv64/lib/math/nextafter.inc | 3 +
libclc/clspv64/lib/subnormal_config.cl | 31 +
libcxx/test/std/pstl | 1 -
.../alg.merge/inplace_merge.pass.cpp | 159 +++++
.../pstl/algorithms/alg.merge/merge.pass.cpp | 113 ++++
.../alg.copy/copy_if.pass.cpp | 147 ++++
.../alg.partitions/is_partitioned.pass.cpp | 101 +++
.../alg.partitions/partition.pass.cpp | 178 +++++
.../alg.partitions/partition_copy.pass.cpp | 116 ++++
.../alg.reverse/reverse.pass.cpp | 104 +++
.../alg.reverse/reverse_copy.pass.cpp | 130 ++++
.../copy_move.pass.cpp | 197 ++++++
.../alg.modifying.operations/fill.pass.cpp | 100 +++
.../generate.pass.cpp | 104 +++
.../alg.modifying.operations/remove.pass.cpp | 161 +++++
.../remove_copy.pass.cpp | 91 +++
.../alg.modifying.operations/replace.pass.cpp | 160 +++++
.../replace_copy.pass.cpp | 105 +++
.../alg.modifying.operations/rotate.pass.cpp | 176 +++++
.../rotate_copy.pass.cpp | 146 ++++
.../swap_ranges.pass.cpp | 133 ++++
.../transform_binary.pass.cpp | 122 ++++
.../transform_unary.pass.cpp | 91 +++
.../alg.modifying.operations/unique.pass.cpp | 163 +++++
.../unique_copy_equal.pass.cpp | 135 ++++
.../alg.nonmodifying/adjacent_find.pass.cpp | 114 ++++
.../alg.nonmodifying/all_of.pass.cpp | 117 ++++
.../alg.nonmodifying/any_of.pass.cpp | 103 +++
.../alg.nonmodifying/count.pass.cpp | 108 +++
.../alg.nonmodifying/equal.pass.cpp | 168 +++++
.../algorithms/alg.nonmodifying/find.pass.cpp | 96 +++
.../alg.nonmodifying/find_end.pass.cpp | 123 ++++
.../alg.nonmodifying/find_first_of.pass.cpp | 112 ++++
.../alg.nonmodifying/find_if.pass.cpp | 109 +++
.../alg.nonmodifying/for_each.pass.cpp | 102 +++
.../alg.nonmodifying/mismatch.pass.cpp | 132 ++++
.../alg.nonmodifying/none_of.pass.cpp | 101 +++
.../alg.nonmodifying/nth_element.pass.cpp | 175 +++++
.../alg.nonmodifying/search_n.pass.cpp | 109 +++
.../alg.heap.operations/is_heap.pass.cpp | 146 ++++
.../lexicographical_compare.pass.cpp | 175 +++++
.../alg.min.max/minmax_element.pass.cpp | 192 ++++++
.../alg.set.operations/includes.pass.cpp | 106 +++
.../alg.set.operations/set.pass.cpp | 280 ++++++++
.../algorithms/alg.sorting/is_sorted.pass.cpp | 100 +++
.../alg.sorting/partial_sort.pass.cpp | 149 +++++
.../alg.sorting/partial_sort_copy.pass.cpp | 196 ++++++
.../pstl/algorithms/alg.sorting/sort.pass.cpp | 247 +++++++
libcxx/test/std/pstl/lit.local.cfg | 2 +
.../numeric.ops/adjacent_difference.pass.cpp | 170 +++++
.../pstl/numerics/numeric.ops/reduce.pass.cpp | 114 ++++
.../pstl/numerics/numeric.ops/scan.fail.cpp | 28 +
.../pstl/numerics/numeric.ops/scan.pass.cpp | 201 ++++++
.../numeric.ops/transform_reduce.pass.cpp | 129 ++++
.../numeric.ops/transform_scan.pass.cpp | 177 +++++
.../uninitialized_construct.pass.cpp | 123 ++++
.../uninitialized_copy_move.pass.cpp | 143 ++++
.../uninitialized_fill_destroy.pass.cpp | 93 +++
llvm/lib/Target/ARM/ARMEncodeDecode.cpp | 628 ++++++++++++++++++
llvm/lib/Target/ARM/ARMEncodeDecode.h | 38 ++
llvm/lib/Target/ARM/ARMRandezvousOptions.cpp | 36 +
llvm/lib/Target/ARM/ARMRandezvousOptions.h | 4 +
llvm/lib/Target/ARM/ARMTargetMachine.cpp | 8 +
llvm/lib/Target/ARM/CMakeLists.txt | 4 +
openmp/tools/analyzer/llvm-openmp-analyzer++ | 46 +-
87 files changed, 8557 insertions(+), 17 deletions(-)
mode change 120000 => 100755 clang/test/Driver/Inputs/CUDA-symlinks/usr/bin/ptxas
mode change 120000 => 100755 clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld
mode change 120000 => 100755 clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld
mode change 120000 => 100755 clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld
mode change 120000 => 100755 clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as
mode change 120000 => 100755 clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld
delete mode 120000 libclc/amdgcn-mesa3d
create mode 100644 libclc/amdgcn-mesa3d/lib/SOURCES
create mode 100644 libclc/amdgcn-mesa3d/lib/workitem/get_global_size.cl
create mode 100644 libclc/amdgcn-mesa3d/lib/workitem/get_local_size.cl
create mode 100644 libclc/amdgcn-mesa3d/lib/workitem/get_num_groups.cl
delete mode 120000 libclc/clspv64
create mode 100644 libclc/clspv64/lib/SOURCES
create mode 100644 libclc/clspv64/lib/math/fma.cl
create mode 100644 libclc/clspv64/lib/math/nextafter.cl
create mode 100644 libclc/clspv64/lib/math/nextafter.inc
create mode 100644 libclc/clspv64/lib/subnormal_config.cl
delete mode 120000 libcxx/test/std/pstl
create mode 100644 libcxx/test/std/pstl/algorithms/alg.merge/inplace_merge.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.merge/merge.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.copy/copy_if.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/is_partitioned.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition_copy.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse_copy.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/copy_move.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/fill.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/generate.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove_copy.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace_copy.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate_copy.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/swap_ranges.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_binary.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_unary.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique_copy_equal.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/adjacent_find.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/all_of.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/any_of.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/count.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/equal.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/find.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_end.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_first_of.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_if.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/for_each.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/mismatch.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/none_of.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/nth_element.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.nonmodifying/search_n.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/alg.heap.operations/is_heap.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/alg.min.max/minmax_element.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/includes.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/set.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/is_sorted.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort_copy.pass.cpp
create mode 100644 libcxx/test/std/pstl/algorithms/alg.sorting/sort.pass.cpp
create mode 100644 libcxx/test/std/pstl/lit.local.cfg
create mode 100644 libcxx/test/std/pstl/numerics/numeric.ops/adjacent_difference.pass.cpp
create mode 100644 libcxx/test/std/pstl/numerics/numeric.ops/reduce.pass.cpp
create mode 100644 libcxx/test/std/pstl/numerics/numeric.ops/scan.fail.cpp
create mode 100644 libcxx/test/std/pstl/numerics/numeric.ops/scan.pass.cpp
create mode 100644 libcxx/test/std/pstl/numerics/numeric.ops/transform_reduce.pass.cpp
create mode 100644 libcxx/test/std/pstl/numerics/numeric.ops/transform_scan.pass.cpp
create mode 100644 libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_construct.pass.cpp
create mode 100644 libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_copy_move.pass.cpp
create mode 100644 libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp
create mode 100755 llvm/lib/Target/ARM/ARMEncodeDecode.cpp
create mode 100755 llvm/lib/Target/ARM/ARMEncodeDecode.h
mode change 100644 => 100755 llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
mode change 100644 => 100755 llvm/lib/Target/ARM/ARMRandezvousOptions.h
mode change 100644 => 100755 llvm/lib/Target/ARM/ARMTargetMachine.cpp
mode change 100644 => 100755 llvm/lib/Target/ARM/CMakeLists.txt
mode change 120000 => 100755 openmp/tools/analyzer/llvm-openmp-analyzer++
diff --git a/clang/test/Driver/Inputs/CUDA-symlinks/usr/bin/ptxas b/clang/test/Driver/Inputs/CUDA-symlinks/usr/bin/ptxas
deleted file mode 120000
index 59eefd95a9023c..00000000000000
--- a/clang/test/Driver/Inputs/CUDA-symlinks/usr/bin/ptxas
+++ /dev/null
@@ -1 +0,0 @@
-../../opt/cuda/bin/ptxas
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/CUDA-symlinks/usr/bin/ptxas b/clang/test/Driver/Inputs/CUDA-symlinks/usr/bin/ptxas
new file mode 100755
index 00000000000000..e69de29bb2d1d6
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld
deleted file mode 120000
index 7e0a9cfe2ddbd6..00000000000000
--- a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld
+++ /dev/null
@@ -1 +0,0 @@
-i386-unknown-linux-gnu-ld.gold
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/i386-unknown-linux-gnu-ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld
deleted file mode 120000
index ce36ac093b6176..00000000000000
--- a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld
+++ /dev/null
@@ -1 +0,0 @@
-x86_64-unknown-linux-gnu-ld.gold
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/bin/x86_64-unknown-linux-gnu-ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld
deleted file mode 120000
index 6cd03701cdda78..00000000000000
--- a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld
+++ /dev/null
@@ -1 +0,0 @@
-ld.gold
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/i386-unknown-linux-gnu/bin/ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld
deleted file mode 120000
index 6cd03701cdda78..00000000000000
--- a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld
+++ /dev/null
@@ -1 +0,0 @@
-ld.gold
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/basic_cross_linux_tree/usr/x86_64-unknown-linux-gnu/bin/ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as
deleted file mode 120000
index 0065315cfd1de8..00000000000000
--- a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as
+++ /dev/null
@@ -1 +0,0 @@
-i386-unknown-linux-gnu-as
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/as
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld
deleted file mode 120000
index 9e5574285c70e4..00000000000000
--- a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld
+++ /dev/null
@@ -1 +0,0 @@
-i386-unknown-linux-gnu-ld
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/bin/ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as
deleted file mode 120000
index 2aa12fdef91620..00000000000000
--- a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as
+++ /dev/null
@@ -1 +0,0 @@
-../../bin/i386-unknown-linux-gnu-as
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/as
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld
deleted file mode 120000
index 5aeaff619662a8..00000000000000
--- a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld
+++ /dev/null
@@ -1 +0,0 @@
-../../bin/i386-unknown-linux-gnu-ld
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_32bit_linux_tree/usr/i386-unknown-linux/bin/ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as
deleted file mode 120000
index 477cbc9635fcbf..00000000000000
--- a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as
+++ /dev/null
@@ -1 +0,0 @@
-x86_64-unknown-linux-gnu-as
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/as
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld
deleted file mode 120000
index 5343caf34d8f34..00000000000000
--- a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld
+++ /dev/null
@@ -1 +0,0 @@
-x86_64-unknown-linux-gnu-ld
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/bin/ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as
deleted file mode 120000
index 84a9113f2671f4..00000000000000
--- a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as
+++ /dev/null
@@ -1 +0,0 @@
-../../bin/x86_64-unknown-linux-gnu-as
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/as
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld
deleted file mode 120000
index c417e3afaa4945..00000000000000
--- a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld
+++ /dev/null
@@ -1 +0,0 @@
-../../bin/x86_64-unknown-linux-gnu-ld
\ No newline at end of file
diff --git a/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld
new file mode 100755
index 00000000000000..b23e55619b2ff0
--- /dev/null
+++ b/clang/test/Driver/Inputs/multilib_64bit_linux_tree/usr/x86_64-unknown-linux/bin/ld
@@ -0,0 +1 @@
+#!/bin/true
diff --git a/libclc/amdgcn-mesa3d b/libclc/amdgcn-mesa3d
deleted file mode 120000
index 400782833efe6c..00000000000000
--- a/libclc/amdgcn-mesa3d
+++ /dev/null
@@ -1 +0,0 @@
-amdgcn-amdhsa
\ No newline at end of file
diff --git a/libclc/amdgcn-mesa3d/lib/SOURCES b/libclc/amdgcn-mesa3d/lib/SOURCES
new file mode 100644
index 00000000000000..8224b7721b2ca5
--- /dev/null
+++ b/libclc/amdgcn-mesa3d/lib/SOURCES
@@ -0,0 +1,3 @@
+workitem/get_global_size.cl
+workitem/get_local_size.cl
+workitem/get_num_groups.cl
diff --git a/libclc/amdgcn-mesa3d/lib/workitem/get_global_size.cl b/libclc/amdgcn-mesa3d/lib/workitem/get_global_size.cl
new file mode 100644
index 00000000000000..62bd2ba283523f
--- /dev/null
+++ b/libclc/amdgcn-mesa3d/lib/workitem/get_global_size.cl
@@ -0,0 +1,23 @@
+#include <clc/clc.h>
+
+#if __clang_major__ >= 8
+#define CONST_AS __constant
+#elif __clang_major__ >= 7
+#define CONST_AS __attribute__((address_space(4)))
+#else
+#define CONST_AS __attribute__((address_space(2)))
+#endif
+
+#if __clang_major__ >= 6
+#define __dispatch_ptr __builtin_amdgcn_dispatch_ptr
+#else
+#define __dispatch_ptr __clc_amdgcn_dispatch_ptr
+CONST_AS uchar * __clc_amdgcn_dispatch_ptr(void) __asm("llvm.amdgcn.dispatch.ptr");
+#endif
+
+_CLC_DEF _CLC_OVERLOAD size_t get_global_size(uint dim) {
+ CONST_AS uint *ptr = (CONST_AS uint *)__dispatch_ptr();
+ if (dim < 3)
+ return ptr[3 + dim];
+ return 1;
+}
diff --git a/libclc/amdgcn-mesa3d/lib/workitem/get_local_size.cl b/libclc/amdgcn-mesa3d/lib/workitem/get_local_size.cl
new file mode 100644
index 00000000000000..9f09fd5a16ec66
--- /dev/null
+++ b/libclc/amdgcn-mesa3d/lib/workitem/get_local_size.cl
@@ -0,0 +1,29 @@
+#include <clc/clc.h>
+
+#if __clang_major__ >= 8
+#define CONST_AS __constant
+#elif __clang_major__ >= 7
+#define CONST_AS __attribute__((address_space(4)))
+#else
+#define CONST_AS __attribute__((address_space(2)))
+#endif
+
+#if __clang_major__ >= 6
+#define __dispatch_ptr __builtin_amdgcn_dispatch_ptr
+#else
+#define __dispatch_ptr __clc_amdgcn_dispatch_ptr
+CONST_AS char * __clc_amdgcn_dispatch_ptr(void) __asm("llvm.amdgcn.dispatch.ptr");
+#endif
+
+_CLC_DEF _CLC_OVERLOAD size_t get_local_size(uint dim) {
+ CONST_AS uint *ptr = (CONST_AS uint *)__dispatch_ptr();
+ switch (dim) {
+ case 0:
+ return ptr[1] & 0xffffu;
+ case 1:
+ return ptr[1] >> 16;
+ case 2:
+ return ptr[2] & 0xffffu;
+ }
+ return 1;
+}
diff --git a/libclc/amdgcn-mesa3d/lib/workitem/get_num_groups.cl b/libclc/amdgcn-mesa3d/lib/workitem/get_num_groups.cl
new file mode 100644
index 00000000000000..35dc2218852114
--- /dev/null
+++ b/libclc/amdgcn-mesa3d/lib/workitem/get_num_groups.cl
@@ -0,0 +1,12 @@
+
+#include <clc/clc.h>
+
+_CLC_DEF _CLC_OVERLOAD size_t get_num_groups(uint dim) {
+ size_t global_size = get_global_size(dim);
+ size_t local_size = get_local_size(dim);
+ size_t num_groups = global_size / local_size;
+ if (global_size % local_size != 0) {
+ num_groups++;
+ }
+ return num_groups;
+}
diff --git a/libclc/clspv64 b/libclc/clspv64
deleted file mode 120000
index ea01ba94bc6368..00000000000000
--- a/libclc/clspv64
+++ /dev/null
@@ -1 +0,0 @@
-clspv
\ No newline at end of file
diff --git a/libclc/clspv64/lib/SOURCES b/libclc/clspv64/lib/SOURCES
new file mode 100644
index 00000000000000..0466345cee0271
--- /dev/null
+++ b/libclc/clspv64/lib/SOURCES
@@ -0,0 +1,48 @@
+subnormal_config.cl
+../../generic/lib/geometric/distance.cl
+../../generic/lib/geometric/length.cl
+math/fma.cl
+math/nextafter.cl
+../../generic/lib/math/acosh.cl
+../../generic/lib/math/asinh.cl
+../../generic/lib/math/atan.cl
+../../generic/lib/math/atan2.cl
+../../generic/lib/math/atan2pi.cl
+../../generic/lib/math/atanh.cl
+../../generic/lib/math/atanpi.cl
+../../generic/lib/math/cbrt.cl
+../../generic/lib/math/clc_fmod.cl
+../../generic/lib/math/clc_hypot.cl
+../../generic/lib/math/clc_ldexp.cl
+../../generic/lib/math/clc_nextafter.cl
+../../generic/lib/math/clc_remainder.cl
+../../generic/lib/math/clc_remquo.cl
+../../generic/lib/math/clc_rootn.cl
+../../generic/lib/math/clc_sqrt.cl
+../../generic/lib/math/clc_tan.cl
+../../generic/lib/math/erf.cl
+../../generic/lib/math/erfc.cl
+../../generic/lib/math/fmod.cl
+../../generic/lib/math/fract.cl
+../../generic/lib/math/frexp.cl
+../../generic/lib/math/half_divide.cl
+../../generic/lib/math/half_recip.cl
+../../generic/lib/math/half_sqrt.cl
+../../generic/lib/math/hypot.cl
+../../generic/lib/math/ilogb.cl
+../../generic/lib/math/ldexp.cl
+../../generic/lib/math/lgamma.cl
+../../generic/lib/math/lgamma_r.cl
+../../generic/lib/math/logb.cl
+../../generic/lib/math/maxmag.cl
+../../generic/lib/math/minmag.cl
+../../generic/lib/math/modf.cl
+../../generic/lib/math/nan.cl
+../../generic/lib/math/remainder.cl
+../../generic/lib/math/remquo.cl
+../../generic/lib/math/rootn.cl
+../../generic/lib/math/rsqrt.cl
+../../generic/lib/math/sqrt.cl
+../../generic/lib/math/tables.cl
+../../generic/lib/math/tanh.cl
+../../generic/lib/math/tgamma.cl
diff --git a/libclc/clspv64/lib/math/fma.cl b/libclc/clspv64/lib/math/fma.cl
new file mode 100644
index 00000000000000..fdc8b8b296876c
--- /dev/null
+++ b/libclc/clspv64/lib/math/fma.cl
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2014 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// This version is derived from the generic fma software implementation
+// (__clc_sw_fma), but avoids the use of ulong in favor of uint2. The logic has
+// been updated as appropriate.
+
+#include <clc/clc.h>
+#include "../../../generic/lib/clcmacro.h"
+#include "../../../generic/lib/math/math.h"
+
+struct fp {
+ uint2 mantissa;
+ int exponent;
+ uint sign;
+};
+
+_CLC_DEF _CLC_OVERLOAD float fma(float a, float b, float c) {
+ /* special cases */
+ if (isnan(a) || isnan(b) || isnan(c) || isinf(a) || isinf(b)) {
+ return mad(a, b, c);
+ }
+
+ /* If only c is inf, and both a,b are regular numbers, the result is c*/
+ if (isinf(c)) {
+ return c;
+ }
+
+ a = __clc_flush_denormal_if_not_supported(a);
+ b = __clc_flush_denormal_if_not_supported(b);
+ c = __clc_flush_denormal_if_not_supported(c);
+
+ if (a == 0.0f || b == 0.0f) {
+ return c;
+ }
+
+ if (c == 0) {
+ return a * b;
+ }
+
+ struct fp st_a, st_b, st_c;
+
+ st_a.exponent = a == .0f ? 0 : ((as_uint(a) & 0x7f800000) >> 23) - 127;
+ st_b.exponent = b == .0f ? 0 : ((as_uint(b) & 0x7f800000) >> 23) - 127;
+ st_c.exponent = c == .0f ? 0 : ((as_uint(c) & 0x7f800000) >> 23) - 127;
+
+ st_a.mantissa.lo = a == .0f ? 0 : (as_uint(a) & 0x7fffff) | 0x800000;
+ st_b.mantissa.lo = b == .0f ? 0 : (as_uint(b) & 0x7fffff) | 0x800000;
+ st_c.mantissa.lo = c == .0f ? 0 : (as_uint(c) & 0x7fffff) | 0x800000;
+ st_a.mantissa.hi = 0;
+ st_b.mantissa.hi = 0;
+ st_c.mantissa.hi = 0;
+
+ st_a.sign = as_uint(a) & 0x80000000;
+ st_b.sign = as_uint(b) & 0x80000000;
+ st_c.sign = as_uint(c) & 0x80000000;
+
+ // Multiplication.
+ // Move the product to the highest bits to maximize precision
+ // mantissa is 24 bits => product is 48 bits, 2bits non-fraction.
+ // Add one bit for future addition overflow,
+ // add another bit to detect subtraction underflow
+ struct fp st_mul;
+ st_mul.sign = st_a.sign ^ st_b.sign;
+ st_mul.mantissa.hi = mul_hi(st_a.mantissa.lo, st_b.mantissa.lo);
+ st_mul.mantissa.lo = st_a.mantissa.lo * st_b.mantissa.lo;
+ uint upper_14bits = (st_mul.mantissa.lo >> 18) & 0x3fff;
+ st_mul.mantissa.lo <<= 14;
+ st_mul.mantissa.hi <<= 14;
+ st_mul.mantissa.hi |= upper_14bits;
+ st_mul.exponent = (st_mul.mantissa.lo != 0 || st_mul.mantissa.hi != 0)
+ ? st_a.exponent + st_b.exponent
+ : 0;
+
+// Mantissa is 23 fractional bits, shift it the same way as product mantissa
+#define C_ADJUST 37ul
+
+ // both exponents are bias adjusted
+ int exp_diff = st_mul.exponent - st_c.exponent;
+
+ uint abs_exp_diff = abs(exp_diff);
+ st_c.mantissa.hi = (st_c.mantissa.lo << 5);
+ st_c.mantissa.lo = 0;
+ uint2 cutoff_bits = (uint2)(0, 0);
+ uint2 cutoff_mask = (uint2)(0, 0);
+ if (abs_exp_diff < 32) {
+ cutoff_mask.lo = (1u << abs(exp_diff)) - 1u;
+ } else if (abs_exp_diff < 64) {
+ cutoff_mask.lo = 0xffffffff;
+ uint remaining = abs_exp_diff - 32;
+ cutoff_mask.hi = (1u << remaining) - 1u;
+ } else {
+ cutoff_mask = (uint2)(0, 0);
+ }
+ uint2 tmp = (exp_diff > 0) ? st_c.mantissa : st_mul.mantissa;
+ if (abs_exp_diff > 0) {
+ cutoff_bits = abs_exp_diff >= 64 ? tmp : (tmp & cutoff_mask);
+ if (abs_exp_diff < 32) {
+ // shift some of the hi bits into the shifted lo bits.
+ uint shift_mask = (1u << abs_exp_diff) - 1;
+ uint upper_saved_bits = tmp.hi & shift_mask;
+ upper_saved_bits = upper_saved_bits << (32 - abs_exp_diff);
+ tmp.hi >>= abs_exp_diff;
+ tmp.lo >>= abs_exp_diff;
+ tmp.lo |= upper_saved_bits;
+ } else if (abs_exp_diff < 64) {
+ tmp.lo = (tmp.hi >> (abs_exp_diff - 32));
+ tmp.hi = 0;
+ } else {
+ tmp = (uint2)(0, 0);
+ }
+ }
+ if (exp_diff > 0)
+ st_c.mantissa = tmp;
+ else
+ st_mul.mantissa = tmp;
+
+ struct fp st_fma;
+ st_fma.sign = st_mul.sign;
+ st_fma.exponent = max(st_mul.exponent, st_c.exponent);
+ st_fma.mantissa = (uint2)(0, 0);
+ if (st_c.sign == st_mul.sign) {
+ uint carry = (hadd(st_mul.mantissa.lo, st_c.mantissa.lo) >> 31) & 0x1;
+ st_fma.mantissa = st_mul.mantissa + st_c.mantissa;
+ st_fma.mantissa.hi += carry;
+ } else {
+ // cutoff bits borrow one
+ uint cutoff_borrow = ((cutoff_bits.lo != 0 || cutoff_bits.hi != 0) &&
+ (st_mul.exponent > st_c.exponent))
+ ? 1
+ : 0;
+ uint borrow = 0;
+ if (st_c.mantissa.lo > st_mul.mantissa.lo) {
+ borrow = 1;
+ } else if (st_c.mantissa.lo == UINT_MAX && cutoff_borrow == 1) {
+ borrow = 1;
+ } else if ((st_c.mantissa.lo + cutoff_borrow) > st_mul.mantissa.lo) {
+ borrow = 1;
+ }
+
+ st_fma.mantissa.lo = st_mul.mantissa.lo - st_c.mantissa.lo - cutoff_borrow;
+ st_fma.mantissa.hi = st_mul.mantissa.hi - st_c.mantissa.hi - borrow;
+ }
+
+ // underflow: st_c.sign != st_mul.sign, and magnitude switches the sign
+ if (st_fma.mantissa.hi > INT_MAX) {
+ st_fma.mantissa = ~st_fma.mantissa;
+ uint carry = (hadd(st_fma.mantissa.lo, 1u) >> 31) & 0x1;
+ st_fma.mantissa.lo += 1;
+ st_fma.mantissa.hi += carry;
+
+ st_fma.sign = st_mul.sign ^ 0x80000000;
+ }
+
+ // detect overflow/underflow
+ uint leading_zeroes = clz(st_fma.mantissa.hi);
+ if (leading_zeroes == 32) {
+ leading_zeroes += clz(st_fma.mantissa.lo);
+ }
+ int overflow_bits = 3 - leading_zeroes;
+
+ // adjust exponent
+ st_fma.exponent += overflow_bits;
+
+ // handle underflow
+ if (overflow_bits < 0) {
+ uint shift = -overflow_bits;
+ if (shift < 32) {
+ uint shift_mask = (1u << shift) - 1;
+ uint saved_lo_bits = (st_fma.mantissa.lo >> (32 - shift)) & shift_mask;
+ st_fma.mantissa.lo <<= shift;
+ st_fma.mantissa.hi <<= shift;
+ st_fma.mantissa.hi |= saved_lo_bits;
+ } else if (shift < 64) {
+ st_fma.mantissa.hi = (st_fma.mantissa.lo << (64 - shift));
+ st_fma.mantissa.lo = 0;
+ } else {
+ st_fma.mantissa = (uint2)(0, 0);
+ }
+
+ overflow_bits = 0;
+ }
+
+ // rounding
+ // overflow_bits is now in the range of [0, 3] making the shift greater than
+ // 32 bits.
+ uint2 trunc_mask;
+ uint trunc_shift = C_ADJUST + overflow_bits - 32;
+ trunc_mask.hi = (1u << trunc_shift) - 1;
+ trunc_mask.lo = UINT_MAX;
+ uint2 trunc_bits = st_fma.mantissa & trunc_mask;
+ trunc_bits.lo |= (cutoff_bits.hi != 0 || cutoff_bits.lo != 0) ? 1 : 0;
+ uint2 last_bit;
+ last_bit.lo = 0;
+ last_bit.hi = st_fma.mantissa.hi & (1u << trunc_shift);
+ uint grs_shift = C_ADJUST - 3 + overflow_bits - 32;
+ uint2 grs_bits;
+ grs_bits.lo = 0;
+ grs_bits.hi = 0x4u << grs_shift;
+
+ // round to nearest even
+ if ((trunc_bits.hi > grs_bits.hi ||
+ (trunc_bits.hi == grs_bits.hi && trunc_bits.lo > grs_bits.lo)) ||
+ (trunc_bits.hi == grs_bits.hi && trunc_bits.lo == grs_bits.lo &&
+ last_bit.hi != 0)) {
+ uint shift = C_ADJUST + overflow_bits - 32;
+ st_fma.mantissa.hi += 1u << shift;
+ }
+
+ // Shift mantissa back to bit 23
+ st_fma.mantissa.lo = (st_fma.mantissa.hi >> (C_ADJUST + overflow_bits - 32));
+ st_fma.mantissa.hi = 0;
+
+ // Detect rounding overflow
+ if (st_fma.mantissa.lo > 0xffffff) {
+ ++st_fma.exponent;
+ st_fma.mantissa.lo >>= 1;
+ }
+
+ if (st_fma.mantissa.lo == 0) {
+ return 0.0f;
+ }
+
+ // Flating point range limit
+ if (st_fma.exponent > 127) {
+ return as_float(as_uint(INFINITY) | st_fma.sign);
+ }
+
+ // Flush denormals
+ if (st_fma.exponent <= -127) {
+ return as_float(st_fma.sign);
+ }
+
+ return as_float(st_fma.sign | ((st_fma.exponent + 127) << 23) |
+ ((uint)st_fma.mantissa.lo & 0x7fffff));
+}
+_CLC_TERNARY_VECTORIZE(_CLC_DEF _CLC_OVERLOAD, float, fma, float, float, float)
diff --git a/libclc/clspv64/lib/math/nextafter.cl b/libclc/clspv64/lib/math/nextafter.cl
new file mode 100644
index 00000000000000..f05e7482136203
--- /dev/null
+++ b/libclc/clspv64/lib/math/nextafter.cl
@@ -0,0 +1,5 @@
+#include <clc/clc.h>
+#include <math/clc_nextafter.h>
+
+#define __CLC_BODY <nextafter.inc>
+#include <clc/math/gentype.inc>
diff --git a/libclc/clspv64/lib/math/nextafter.inc b/libclc/clspv64/lib/math/nextafter.inc
new file mode 100644
index 00000000000000..ee39be53b1e17c
--- /dev/null
+++ b/libclc/clspv64/lib/math/nextafter.inc
@@ -0,0 +1,3 @@
+_CLC_DEF _CLC_OVERLOAD __CLC_GENTYPE nextafter(__CLC_GENTYPE x, __CLC_GENTYPE y) {
+ return __clc_nextafter(x, y);
+}
diff --git a/libclc/clspv64/lib/subnormal_config.cl b/libclc/clspv64/lib/subnormal_config.cl
new file mode 100644
index 00000000000000..167fe1b1a1bcfd
--- /dev/null
+++ b/libclc/clspv64/lib/subnormal_config.cl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <clc/clc.h>
+
+#include "config.h"
+
+_CLC_DEF bool __clc_fp16_subnormals_supported() { return false; }
+
+_CLC_DEF bool __clc_fp32_subnormals_supported() { return false; }
+
+_CLC_DEF bool __clc_fp64_subnormals_supported() { return false; }
diff --git a/libcxx/test/std/pstl b/libcxx/test/std/pstl
deleted file mode 120000
index 27a2822d956696..00000000000000
--- a/libcxx/test/std/pstl
+++ /dev/null
@@ -1 +0,0 @@
-../../../pstl/test/std
\ No newline at end of file
diff --git a/libcxx/test/std/pstl/algorithms/alg.merge/inplace_merge.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.merge/inplace_merge.pass.cpp
new file mode 100644
index 00000000000000..3446d955093add
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.merge/inplace_merge.pass.cpp
@@ -0,0 +1,159 @@
+// -*- C++ -*-
+//===-- inplace_merge.pass.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename BiDirIt1, typename Size, typename Generator1, typename Generator2, typename Compare>
+ void
+ operator()(pstl::execution::unsequenced_policy, BiDirIt1 first1, BiDirIt1 last1, BiDirIt1 first2, BiDirIt1 last2,
+ Size n, Size m, Generator1 generator1, Generator2 generator2, Compare comp)
+ {
+ }
+
+ template <typename BiDirIt1, typename Size, typename Generator1, typename Generator2, typename Compare>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, BiDirIt1 first1, BiDirIt1 last1, BiDirIt1 first2,
+ BiDirIt1 last2, Size n, Size m, Generator1 generator1, Generator2 generator2, Compare comp)
+ {
+ }
+#endif
+
+ // inplace_merge works with bidirectional iterators at least
+ template <typename Policy, typename BiDirIt1, typename Size, typename Generator1, typename Generator2,
+ typename Compare>
+ typename std::enable_if<!is_same_iterator_category<BiDirIt1, std::forward_iterator_tag>::value, void>::type
+ operator()(Policy&& exec, BiDirIt1 first1, BiDirIt1 last1, BiDirIt1 first2, BiDirIt1 last2, Size n, Size m,
+ Generator1 generator1, Generator2 generator2, Compare comp)
+ {
+ const BiDirIt1 mid1 = std::next(first1, m);
+ fill_data(first1, mid1, generator1);
+ fill_data(mid1, last1, generator2);
+
+ const BiDirIt1 mid2 = std::next(first2, m);
+ fill_data(first2, mid2, generator1);
+ fill_data(mid2, last2, generator2);
+
+ std::inplace_merge(first1, mid1, last1, comp);
+ std::inplace_merge(exec, first2, mid2, last2, comp);
+ EXPECT_EQ_N(first1, first2, n, "wrong effect from inplace_merge with predicate");
+ }
+
+ template <typename Policy, typename BiDirIt1, typename Size, typename Generator1, typename Generator2,
+ typename Compare>
+ typename std::enable_if<is_same_iterator_category<BiDirIt1, std::forward_iterator_tag>::value, void>::type
+ operator()(Policy&&, BiDirIt1, BiDirIt1, BiDirIt1, BiDirIt1, Size, Size, Generator1, Generator2, Compare)
+ {
+ }
+};
+
+template <typename T, typename Generator1, typename Generator2, typename Compare>
+void
+test_by_type(Generator1 generator1, Generator2 generator2, Compare comp)
+{
+ using namespace std;
+ size_t max_size = 100000;
+ Sequence<T> in1(max_size, [](size_t v) { return T(v); });
+ Sequence<T> exp(max_size, [](size_t v) { return T(v); });
+ size_t m;
+
+ for (size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ m = 0;
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n, exp.begin(), exp.begin() + n, n, m,
+ generator1, generator2, comp);
+
+ m = n / 3;
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n, exp.begin(), exp.begin() + n, n, m,
+ generator1, generator2, comp);
+
+ m = 2 * n / 3;
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n, exp.begin(), exp.begin() + n, n, m,
+ generator1, generator2, comp);
+ }
+}
+
+template <typename T>
+struct LocalWrapper
+{
+ explicit LocalWrapper(int32_t k) : my_val(k) {}
+ LocalWrapper(LocalWrapper&& input) { my_val = std::move(input.my_val); }
+ LocalWrapper&
+ operator=(LocalWrapper&& input)
+ {
+ my_val = std::move(input.my_val);
+ return *this;
+ }
+ bool
+ operator<(const LocalWrapper<T>& w) const
+ {
+ return my_val < w.my_val;
+ }
+ friend bool
+ operator==(const LocalWrapper<T>& x, const LocalWrapper<T>& y)
+ {
+ return x.my_val == y.my_val;
+ }
+ friend std::ostream&
+ operator<<(std::ostream& stream, const LocalWrapper<T>& input)
+ {
+ return stream << input.my_val;
+ }
+
+ private:
+ T my_val;
+};
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ invoke_if(exec, [&]() { inplace_merge(exec, iter, iter, iter, non_const(std::less<T>())); });
+ }
+};
+
+int
+main()
+{
+ test_by_type<float64_t>([](int32_t i) { return -2 * i; }, [](int32_t i) { return -(2 * i + 1); },
+ [](const float64_t x, const float64_t y) { return x > y; });
+
+ test_by_type<int32_t>([](int32_t i) { return 10 * i; }, [](int32_t i) { return i + 1; }, std::less<int32_t>());
+
+ test_by_type<LocalWrapper<float32_t>>([](int32_t i) { return LocalWrapper<float32_t>(2 * i + 1); },
+ [](int32_t i) { return LocalWrapper<float32_t>(2 * i); },
+ std::less<LocalWrapper<float32_t>>());
+
+ test_algo_basic_single<int32_t>(run_for_rnd_bi<test_non_const<int32_t>>());
+
+ test_by_type<MemoryChecker>(
+ [](std::size_t idx){ return MemoryChecker{std::int32_t(idx * 2)}; },
+ [](std::size_t idx){ return MemoryChecker{std::int32_t(idx * 2 + 1)}; },
+ [](const MemoryChecker& val1, const MemoryChecker& val2){ return val1.value() == val2.value(); });
+ EXPECT_FALSE(MemoryChecker::alive_objects() < 0, "wrong effect from inplace_merge: number of ctors calls < num of dtors calls");
+ EXPECT_FALSE(MemoryChecker::alive_objects() > 0, "wrong effect from inplace_merge: number of ctors calls > num of dtors calls");
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.merge/merge.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.merge/merge.pass.cpp
new file mode 100644
index 00000000000000..a09ef4223bf774
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.merge/merge.pass.cpp
@@ -0,0 +1,113 @@
+// -*- C++ -*-
+//===-- merge.pass.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+#include <functional>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_merge
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename OutputIterator,
+ typename Compare>
+ void
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
+ OutputIterator out_first, OutputIterator out_last, Compare comp)
+ {
+ using namespace std;
+ {
+ const auto res = merge(exec, first1, last1, first2, last2, out_first, comp);
+ EXPECT_TRUE(res == out_last, "wrong return result from merge with predicate");
+ EXPECT_TRUE(is_sorted(out_first, res, comp), "wrong result from merge with predicate");
+ EXPECT_TRUE(includes(out_first, res, first1, last1, comp), "first sequence is not a part of result");
+ EXPECT_TRUE(includes(out_first, res, first2, last2, comp), "second sequence is not a part of result");
+ }
+ {
+ const auto res = merge(exec, first1, last1, first2, last2, out_first);
+ EXPECT_TRUE(res == out_last, "wrong return result from merge");
+ EXPECT_TRUE(is_sorted(out_first, res), "wrong result from merge");
+ }
+ }
+
+ // for reverse iterators
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename OutputIterator,
+ typename Compare>
+ void
+ operator()(Policy&& exec, std::reverse_iterator<InputIterator1> first1, std::reverse_iterator<InputIterator1> last1,
+ std::reverse_iterator<InputIterator2> first2, std::reverse_iterator<InputIterator2> last2,
+ std::reverse_iterator<OutputIterator> out_first, std::reverse_iterator<OutputIterator> out_last, Compare)
+ {
+ using namespace std;
+ typedef typename std::iterator_traits<std::reverse_iterator<InputIterator1>>::value_type T;
+ const auto res = merge(exec, first1, last1, first2, last2, out_first, std::greater<T>());
+
+ EXPECT_TRUE(res == out_last, "wrong return result from merge with predicate");
+ EXPECT_TRUE(is_sorted(out_first, res, std::greater<T>()), "wrong result from merge with predicate");
+ EXPECT_TRUE(includes(out_first, res, first1, last1, std::greater<T>()),
+ "first sequence is not a part of result");
+ EXPECT_TRUE(includes(out_first, res, first2, last2, std::greater<T>()),
+ "second sequence is not a part of result");
+ }
+};
+
+template <typename T, typename Generator1, typename Generator2>
+void
+test_merge_by_type(Generator1 generator1, Generator2 generator2)
+{
+ using namespace std;
+ size_t max_size = 100000;
+ Sequence<T> in1(max_size, generator1);
+ Sequence<T> in2(max_size / 2, generator2);
+ Sequence<T> out(in1.size() + in2.size());
+ std::sort(in1.begin(), in1.end());
+ std::sort(in2.begin(), in2.end());
+
+ for (size_t size = 0; size <= max_size; size = size <= 16 ? size + 1 : size_t(3.1415 * size))
+ {
+ invoke_on_all_policies(test_merge(), in1.cbegin(), in1.cbegin() + size, in2.data(), in2.data() + size / 2,
+ out.begin(), out.begin() + 1.5 * size, std::less<T>());
+ invoke_on_all_policies(test_merge(), in1.data(), in1.data() + size, in2.cbegin(), in2.cbegin() + size / 2,
+ out.begin(), out.begin() + 3 * size / 2, std::less<T>());
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter)
+ {
+ merge(exec, input_iter, input_iter, input_iter, input_iter, out_iter, non_const(std::less<T>()));
+ }
+};
+
+int
+main()
+{
+ test_merge_by_type<int32_t>([](size_t v) { return (v % 2 == 0 ? v : -v) * 3; }, [](size_t v) { return v * 2; });
+ test_merge_by_type<float64_t>([](size_t v) { return float64_t(v); }, [](size_t v) { return float64_t(v - 100); });
+
+#if !defined(_PSTL_ICC_16_17_TEST_64_TIMEOUT)
+ test_merge_by_type<Wrapper<int16_t>>([](size_t v) { return Wrapper<int16_t>(v % 100); },
+ [](size_t v) { return Wrapper<int16_t>(v % 10); });
+#endif
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.copy/copy_if.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.copy/copy_if.pass.cpp
new file mode 100644
index 00000000000000..692907e0f92e25
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.copy/copy_if.pass.cpp
@@ -0,0 +1,147 @@
+// -*- C++ -*-
+//===-- copy_if.pass.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for copy_if and remove_copy_if
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct run_copy_if
+{
+#if defined(_PSTL_ICC_16_VC14_TEST_PAR_TBB_RT_RELEASE_64_BROKEN) // dummy specializations to skip testing in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(pstl::execution::parallel_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2 expected_last, Size n,
+ Predicate pred, T trash)
+ {
+ }
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator2 expected_first,
+ OutputIterator2 expected_last, Size n, Predicate pred, T trash)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2, Size n, Predicate pred,
+ T trash)
+ {
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+
+ // Run copy_if
+ auto i = copy_if(first, last, expected_first, pred);
+ auto k = copy_if(exec, first, last, out_first, pred);
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong copy_if effect");
+ for (size_t j = 0; j < GuardSize; ++j)
+ {
+ ++k;
+ }
+ EXPECT_TRUE(out_last == k, "wrong return value from copy_if");
+
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+ // Run remove_copy_if
+ i = remove_copy_if(first, last, expected_first, [=](const T& x) { return !pred(x); });
+ k = remove_copy_if(exec, first, last, out_first, [=](const T& x) { return !pred(x); });
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong remove_copy_if effect");
+ for (size_t j = 0; j < GuardSize; ++j)
+ {
+ ++k;
+ }
+ EXPECT_TRUE(out_last == k, "wrong return value from remove_copy_if");
+ }
+};
+
+template <typename T, typename Predicate, typename Convert>
+void
+test(T trash, Predicate pred, Convert convert, bool check_weakness = true)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ // count is number of output elements, plus a handful
+ // more for sake of detecting buffer overruns.
+ size_t count = GuardSize;
+ Sequence<T> in(n, [&](size_t k) -> T {
+ T val = convert(n ^ k);
+ count += pred(val) ? 1 : 0;
+ return val;
+ });
+
+ Sequence<T> out(count, [=](size_t) { return trash; });
+ Sequence<T> expected(count, [=](size_t) { return trash; });
+ if (check_weakness)
+ {
+ auto expected_result = copy_if(in.cfbegin(), in.cfend(), expected.begin(), pred);
+ size_t m = expected_result - expected.begin();
+ EXPECT_TRUE(n / 4 <= m && m <= 3 * (n + 1) / 4, "weak test for copy_if");
+ }
+ invoke_on_all_policies(run_copy_if(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), count, pred, trash);
+ invoke_on_all_policies(run_copy_if(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), count, pred, trash);
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ copy_if(exec, input_iter, input_iter, out_iter, non_const(is_even));
+
+ invoke_if(exec, [&]() { remove_copy_if(exec, input_iter, input_iter, out_iter, non_const(is_even)); });
+ }
+};
+
+int
+main()
+{
+ test<float64_t>(-666.0, [](const float64_t& x) { return x * x <= 1024; },
+ [](size_t j) { return ((j + 1) % 7 & 2) != 0 ? float64_t(j % 32) : float64_t(j % 33 + 34); });
+
+ test<int32_t>(-666, [](const int32_t& x) { return x != 42; },
+ [](size_t j) { return ((j + 1) % 5 & 2) != 0 ? int32_t(j + 1) : 42; });
+
+#if !defined(_PSTL_ICC_17_TEST_MAC_RELEASE_32_BROKEN)
+ test<Number>(Number(42, OddTag()), IsMultiple(3, OddTag()), [](int32_t j) { return Number(j, OddTag()); });
+#endif
+
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_RELEASE_BROKEN)
+ test<int32_t>(-666, [](const int32_t&) { return true; }, [](size_t j) { return j; }, false);
+#endif
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/is_partitioned.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/is_partitioned.pass.cpp
new file mode 100644
index 00000000000000..bd6b259c4d0afd
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/is_partitioned.pass.cpp
@@ -0,0 +1,101 @@
+// -*- C++ -*-
+//===-- is_partitioned.pass.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+ //dummy specialization by policy type, in case of broken configuration
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN)
+
+ template <typename Iterator1, typename Predicate>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator1 begin1, Iterator1 end1, Predicate pred)
+ {
+ }
+ template <typename Iterator1, typename Predicate>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 begin1, Iterator1 end1, Predicate pred)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 begin1, Iterator1 end1, Predicate pred)
+ {
+ const bool expected = std::is_partitioned(begin1, end1, pred);
+ const bool actual = std::is_partitioned(exec, begin1, end1, pred);
+ EXPECT_TRUE(actual == expected, "wrong return result from is_partitioned");
+ }
+};
+
+template <typename T, typename Predicate>
+void
+test(Predicate pred)
+{
+
+ const std::size_t max_n = 1000000;
+ Sequence<T> in(max_n, [](std::size_t k) { return T(k); });
+
+ for (std::size_t n1 = 0; n1 <= max_n; n1 = n1 <= 16 ? n1 + 1 : std::size_t(3.1415 * n1))
+ {
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.begin() + n1, pred);
+ std::partition(in.begin(), in.begin() + n1, pred);
+ invoke_on_all_policies(test_one_policy(), in.cbegin(), in.cbegin() + n1, pred);
+ }
+}
+
+template <typename T>
+struct LocalWrapper
+{
+ explicit LocalWrapper(std::size_t k) : my_val(k) {}
+
+ private:
+ T my_val;
+};
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ invoke_if(exec, [&]() { is_partitioned(exec, iter, iter, non_const(is_even)); });
+ }
+};
+
+int
+main()
+{
+ test<float64_t>([](const float64_t x) { return x < 0; });
+ test<int32_t>([](const int32_t x) { return x > 1000; });
+ test<uint16_t>([](const uint16_t x) { return x % 5 < 3; });
+#if !defined(_PSTL_ICC_18_TEST_EARLY_EXIT_MONOTONIC_RELEASE_BROKEN) && !defined(_PSTL_ICC_19_TEST_IS_PARTITIONED_RELEASE_BROKEN)
+ test<LocalWrapper<float64_t>>([](const LocalWrapper<float64_t>&) { return true; });
+#endif
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp
new file mode 100644
index 00000000000000..724f0ba6d3f4d9
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition.pass.cpp
@@ -0,0 +1,178 @@
+// -*- C++ -*-
+//===-- partition.pass.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for stable_partition and partition
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct DataType
+{
+ explicit DataType(int32_t k) : my_val(k) {}
+ DataType(DataType&& input) { my_val = std::move(input.my_val); }
+ DataType&
+ operator=(DataType&& input)
+ {
+ my_val = std::move(input.my_val);
+ return *this;
+ }
+ T
+ get_val() const
+ {
+ return my_val;
+ }
+
+ friend std::ostream&
+ operator<<(std::ostream& stream, const DataType<T>& input)
+ {
+ return stream << input.my_val;
+ }
+
+ private:
+ T my_val;
+};
+
+template <typename Iterator>
+typename std::enable_if<std::is_trivial<typename std::iterator_traits<Iterator>::value_type>::value, bool>::type
+is_equal(Iterator first, Iterator last, Iterator d_first)
+{
+ return std::equal(first, last, d_first);
+}
+
+template <typename Iterator>
+typename std::enable_if<!std::is_trivial<typename std::iterator_traits<Iterator>::value_type>::value, bool>::type
+ is_equal(Iterator, Iterator, Iterator)
+{
+ return true;
+}
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specializations to skip testing in case of broken configuration
+ template <typename BiDirIt, typename Size, typename UnaryOp, typename Generator>
+ void
+ operator()(pstl::execution::unsequenced_policy, BiDirIt first, BiDirIt last, BiDirIt exp_first, BiDirIt exp_last,
+ Size n, UnaryOp unary_op, Generator generator)
+ {
+ }
+
+ template <typename BiDirIt, typename Size, typename UnaryOp, typename Generator>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, BiDirIt first, BiDirIt last, BiDirIt exp_first,
+ BiDirIt exp_last, Size n, UnaryOp unary_op, Generator generator)
+ {
+ }
+#elif defined(_PSTL_ICC_16_VC14_TEST_PAR_TBB_RT_RELEASE_64_BROKEN) //dummy specializations to skip testing in case of broken configuration
+ template <typename BiDirIt, typename Size, typename UnaryOp, typename Generator>
+ void
+ operator()(pstl::execution::parallel_policy, BiDirIt first, BiDirIt last, BiDirIt exp_first, BiDirIt exp_last,
+ Size n, UnaryOp unary_op, Generator generator)
+ {
+ }
+
+ template <typename BiDirIt, typename Size, typename UnaryOp, typename Generator>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, BiDirIt first, BiDirIt last, BiDirIt exp_first,
+ BiDirIt exp_last, Size n, UnaryOp unary_op, Generator generator)
+ {
+ }
+#endif
+
+ template <typename Policy, typename BiDirIt, typename Size, typename UnaryOp, typename Generator>
+ typename std::enable_if<!is_same_iterator_category<BiDirIt, std::forward_iterator_tag>::value, void>::type
+ operator()(Policy&& exec, BiDirIt first, BiDirIt last, BiDirIt exp_first, BiDirIt exp_last, Size, UnaryOp unary_op,
+ Generator generator)
+ {
+ // partition
+ {
+ fill_data(first, last, generator);
+ BiDirIt actual_ret = std::partition(exec, first, last, unary_op);
+ EXPECT_TRUE(std::all_of(first, actual_ret, unary_op) && !std::any_of(actual_ret, last, unary_op),
+ "wrong effect from partition");
+ }
+ // stable_partition
+ {
+ fill_data(exp_first, exp_last, generator);
+ BiDirIt exp_ret = std::stable_partition(exp_first, exp_last, unary_op);
+ fill_data(first, last, generator);
+ BiDirIt actual_ret = std::stable_partition(exec, first, last, unary_op);
+
+ EXPECT_TRUE(std::distance(first, actual_ret) == std::distance(exp_first, exp_ret),
+ "wrong result from stable_partition");
+ EXPECT_TRUE((is_equal<BiDirIt>(exp_first, exp_last, first)), "wrong effect from stable_partition");
+ }
+ }
+ template <typename Policy, typename BiDirIt, typename Size, typename UnaryOp, typename Generator>
+ typename std::enable_if<is_same_iterator_category<BiDirIt, std::forward_iterator_tag>::value, void>::type
+ operator()(Policy&&, BiDirIt, BiDirIt, BiDirIt, BiDirIt, Size, UnaryOp, Generator)
+ {
+ }
+};
+
+template <typename T, typename Generator, typename UnaryPred>
+void
+test_by_type(Generator generator, UnaryPred pred)
+{
+
+ using namespace std;
+ size_t max_size = 100000;
+ Sequence<T> in(max_size, [](size_t v) { return T(v); });
+ Sequence<T> exp(max_size, [](size_t v) { return T(v); });
+
+ for (size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.begin() + n, exp.begin(), exp.begin() + n, n, pred,
+ generator);
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ invoke_if(exec, [&]() {
+ partition(exec, iter, iter, non_const(is_even));
+ stable_partition(exec, iter, iter, non_const(is_even));
+ });
+ }
+};
+
+int
+main()
+{
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_RELEASE_BROKEN)
+ test_by_type<int32_t>([](int32_t i) { return i; }, [](int32_t) { return true; });
+#endif
+ test_by_type<float64_t>([](int32_t i) { return -i; }, [](const float64_t x) { return x < 0; });
+ test_by_type<int64_t>([](int32_t i) { return i + 1; }, [](int64_t x) { return x % 3 == 0; });
+ test_by_type<DataType<float32_t>>([](int32_t i) { return DataType<float32_t>(2 * i + 1); },
+ [](const DataType<float32_t>& x) { return x.get_val() < 0; });
+
+ test_algo_basic_single<int32_t>(run_for_rnd_bi<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition_copy.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition_copy.pass.cpp
new file mode 100644
index 00000000000000..c621f8352f1dc7
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.partitions/partition_copy.pass.cpp
@@ -0,0 +1,116 @@
+// -*- C++ -*-
+//===-- partition_copy.pass.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for stable_partition and partition_copy
+#include "support/pstl_test_config.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <execution>
+#include <functional>
+#include <iterator>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_partition_copy
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2,
+ typename UnaryOp>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator true_first, OutputIterator,
+ OutputIterator2 false_first, OutputIterator2, UnaryOp unary_op)
+ {
+
+ auto actual_ret = std::partition_copy(exec, first, last, true_first, false_first, unary_op);
+
+ EXPECT_TRUE(std::distance(true_first, actual_ret.first) == std::count_if(first, last, unary_op),
+ "partition_copy has wrong effect from true sequence");
+ EXPECT_TRUE(std::distance(false_first, actual_ret.second) == std::count_if(first, last, std::not_fn(unary_op)),
+ "partition_copy has wrong effect from false sequence");
+ }
+
+ //dummy specialization by iterator type and policy type, in case of broken configuration
+#if defined(_PSTL_ICC_1800_TEST_MONOTONIC_RELEASE_64_BROKEN)
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename UnaryOp>
+ void
+ operator()(pstl::execution::unsequenced_policy, std::reverse_iterator<InputIterator> first,
+ std::reverse_iterator<InputIterator> last, std::reverse_iterator<OutputIterator> true_first,
+ std::reverse_iterator<OutputIterator> true_last, std::reverse_iterator<OutputIterator2> false_first,
+ OutputIterator2 false_last, UnaryOp unary_op)
+ {
+ }
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename UnaryOp>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, std::reverse_iterator<InputIterator> first,
+ std::reverse_iterator<InputIterator> last, std::reverse_iterator<OutputIterator> true_first,
+ std::reverse_iterator<OutputIterator> true_last, std::reverse_iterator<OutputIterator2> false_first,
+ OutputIterator2 false_last, UnaryOp unary_op)
+ {
+ }
+#endif
+};
+
+template <typename T, typename UnaryPred>
+void
+test(UnaryPred pred)
+{
+
+ const std::size_t max_size = 100000;
+ Sequence<T> in(max_size, [](std::size_t v) -> T { return T(v); });
+ Sequence<T> actual_true(max_size);
+ Sequence<T> actual_false(max_size);
+ for (std::size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : std::size_t(3.1415 * n))
+ {
+
+ // for non-const input iterators
+ invoke_on_all_policies(test_partition_copy(), in.begin(), in.begin() + n, actual_true.begin(),
+ actual_true.begin() + n, actual_false.begin(), actual_false.begin() + n, pred);
+
+ // for const input iterators
+ invoke_on_all_policies(test_partition_copy(), in.cbegin(), in.cbegin() + n, actual_true.begin(),
+ actual_true.begin() + n, actual_false.begin(), actual_false.begin() + n, pred);
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+
+ partition_copy(exec, input_iter, input_iter, out_iter, out_iter, non_const(is_even));
+ }
+};
+
+int
+main()
+{
+ test<int32_t>([](const int32_t value) { return value % 2; });
+
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_RELEASE_BROKEN)
+ test<int32_t>([](const int32_t) { return true; });
+#endif
+
+ test<float64_t>([](const float64_t value) { return value > 2 << 6; });
+ test<Wrapper<float64_t>>([](const Wrapper<float64_t>& value) -> bool { return value.get_my_field() != nullptr; });
+
+ test_algo_basic_double<int32_t>(run_for_rnd_bi<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse.pass.cpp
new file mode 100644
index 00000000000000..126454fd7272eb
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse.pass.cpp
@@ -0,0 +1,104 @@
+// -*- C++ -*-
+//===-- reverse.pass.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iterator>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_18_VC141_TEST_SIMD_LAMBDA_RELEASE_BROKEN) || defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1, typename Iterator2>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::unsequenced_policy, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b,
+ Iterator2 actual_e)
+ {
+ }
+ template <typename Iterator1, typename Iterator2>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b,
+ Iterator2 actual_e)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2>
+ typename std::enable_if<!is_same_iterator_category<Iterator1, std::forward_iterator_tag>::value>::type
+ operator()(ExecutionPolicy&& exec, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b, Iterator2 actual_e)
+ {
+ using namespace std;
+
+ copy(data_b, data_e, actual_b);
+
+ reverse(exec, actual_b, actual_e);
+
+ bool check = equal(data_b, data_e, reverse_iterator<Iterator2>(actual_e));
+
+ EXPECT_TRUE(check, "wrong result of reverse");
+ }
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::forward_iterator_tag>::value>::type
+ operator()(ExecutionPolicy&&, Iterator1, Iterator1, Iterator2, Iterator2)
+ {
+ }
+};
+
+template <typename T>
+void
+test()
+{
+ const std::size_t max_len = 100000;
+
+ Sequence<T> actual(max_len);
+
+ Sequence<T> data(max_len, [](std::size_t i) { return T(i); });
+
+ for (std::size_t len = 0; len < max_len; len = len <= 16 ? len + 1 : std::size_t(3.1415 * len))
+ {
+ invoke_on_all_policies(test_one_policy(), data.begin(), data.begin() + len, actual.begin(),
+ actual.begin() + len);
+ }
+}
+
+template <typename T>
+struct wrapper
+{
+ T t;
+ wrapper() {}
+ explicit wrapper(T t_) : t(t_) {}
+ bool
+ operator==(const wrapper<T>& a) const
+ {
+ return t == a.t;
+ }
+};
+
+int
+main()
+{
+ test<int32_t>();
+ test<uint16_t>();
+ test<float64_t>();
+#if !defined(_PSTL_ICC_17_TEST_MAC_RELEASE_32_BROKEN)
+ test<wrapper<float64_t>>();
+#endif
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse_copy.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse_copy.pass.cpp
new file mode 100644
index 00000000000000..533443896c3586
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/alg.reverse/reverse_copy.pass.cpp
@@ -0,0 +1,130 @@
+// -*- C++ -*-
+//===-- reverse_copy.pass.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iterator>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct wrapper
+{
+ T t;
+ wrapper() {}
+ explicit wrapper(T t_) : t(t_) {}
+ wrapper&
+ operator=(const T& t_)
+ {
+ t = t_;
+ return *this;
+ }
+ bool
+ operator==(const wrapper& t_) const
+ {
+ return t == t_.t;
+ }
+};
+
+template <typename T1, typename T2>
+bool
+eq(const wrapper<T1>& a, const wrapper<T2>& b)
+{
+ return a.t == b.t;
+}
+
+template <typename T1, typename T2>
+bool
+eq(const T1& a, const T2& b)
+{
+ return a == b;
+}
+
+// we need to save state here, because we need to test with different types of iterators
+// due to the caller invoke_on_all_policies does forcing modification passed iterator type to cover additional usage cases.
+template <typename Iterator>
+struct test_one_policy
+{
+ Iterator data_b;
+ Iterator data_e;
+ test_one_policy(Iterator b, Iterator e) : data_b(b), data_e(e) {}
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::unsequenced_policy, Iterator1 actual_b, Iterator1 actual_e)
+ {
+ }
+ template <typename Iterator1>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 actual_b, Iterator1 actual_e)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 actual_b, Iterator1 actual_e)
+ {
+ using namespace std;
+ using T = typename iterator_traits<Iterator1>::value_type;
+
+ fill(actual_b, actual_e, T(-123));
+ Iterator1 actual_return = reverse_copy(exec, data_b, data_e, actual_b);
+
+ EXPECT_TRUE(actual_return == actual_e, "wrong result of reverse_copy");
+
+ const auto n = std::distance(data_b, data_e);
+ Sequence<T> res(n);
+ std::copy(std::reverse_iterator<Iterator>(data_e), std::reverse_iterator<Iterator>(data_b), res.begin());
+
+ EXPECT_EQ_N(res.begin(), actual_b, n, "wrong effect of reverse_copy");
+ }
+};
+
+template <typename T1, typename T2>
+void
+test()
+{
+ typedef typename Sequence<T1>::iterator iterator_type;
+ typedef typename Sequence<T1>::const_bidirectional_iterator cbi_iterator_type;
+
+ const std::size_t max_len = 100000;
+
+ Sequence<T2> actual(max_len);
+
+ Sequence<T1> data(max_len, [](std::size_t i) { return T1(i); });
+
+ for (std::size_t len = 0; len < max_len; len = len <= 16 ? len + 1 : std::size_t(3.1415 * len))
+ {
+ invoke_on_all_policies(test_one_policy<iterator_type>(data.begin(), data.begin() + len), actual.begin(),
+ actual.begin() + len);
+ invoke_on_all_policies(test_one_policy<cbi_iterator_type>(data.cbibegin(), std::next(data.cbibegin(), len)),
+ actual.begin(), actual.begin() + len);
+ }
+}
+
+int
+main()
+{
+ test<int16_t, int8_t>();
+ test<uint16_t, float32_t>();
+ test<float64_t, int64_t>();
+ test<wrapper<float64_t>, wrapper<float64_t>>();
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/copy_move.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/copy_move.pass.cpp
new file mode 100644
index 00000000000000..24b784383916e0
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/copy_move.pass.cpp
@@ -0,0 +1,197 @@
+// -*- C++ -*-
+//===-- copy_move.pass.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for copy, move and copy_n
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct run_copy
+{
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size, typename T>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2 expected_last, Size size,
+ Size n, T trash)
+ {
+ }
+
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size, typename T>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator2 expected_first,
+ OutputIterator2 expected_last, Size size, Size n, T trash)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2, Size size, Size n, T trash)
+ {
+ // Cleaning
+ std::fill_n(expected_first, size, trash);
+ std::fill_n(out_first, size, trash);
+
+ // Run copy
+ copy(first, last, expected_first);
+ auto k = copy(exec, first, last, out_first);
+ for (size_t j = 0; j < GuardSize; ++j)
+ ++k;
+ EXPECT_EQ_N(expected_first, out_first, size, "wrong effect from copy");
+ EXPECT_TRUE(out_last == k, "wrong return value from copy");
+
+ // Cleaning
+ std::fill_n(out_first, size, trash);
+ // Run copy_n
+ k = copy_n(exec, first, n, out_first);
+ for (size_t j = 0; j < GuardSize; ++j)
+ ++k;
+ EXPECT_EQ_N(expected_first, out_first, size, "wrong effect from copy_n");
+ EXPECT_TRUE(out_last == k, "wrong return value from copy_n");
+ }
+};
+
+template <typename T>
+struct run_move
+{
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2 expected_last, Size size,
+ Size n, T trash)
+ {
+ }
+
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator2 expected_first,
+ OutputIterator2 expected_last, Size size, Size n, T trash)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2, Size size, Size, T trash)
+ {
+ // Cleaning
+ std::fill_n(expected_first, size, trash);
+ std::fill_n(out_first, size, trash);
+
+ // Run move
+ move(first, last, expected_first);
+ auto k = move(exec, first, last, out_first);
+ for (size_t j = 0; j < GuardSize; ++j)
+ ++k;
+ EXPECT_EQ_N(expected_first, out_first, size, "wrong effect from move");
+ EXPECT_TRUE(out_last == k, "wrong return value from move");
+ }
+};
+
+template <typename T>
+struct run_move<Wrapper<T>>
+{
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2 expected_last, Size size,
+ Size n, Wrapper<T> trash)
+ {
+ }
+
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator2 expected_first,
+ OutputIterator2 expected_last, Size size, Size n, Wrapper<T> trash)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2, OutputIterator2, Size size, Size, Wrapper<T> trash)
+ {
+ // Cleaning
+ std::fill_n(out_first, size, trash);
+ Wrapper<T>::SetMoveCount(0);
+
+ // Run move
+ auto k = move(exec, first, last, out_first);
+ for (size_t j = 0; j < GuardSize; ++j)
+ ++k;
+ EXPECT_TRUE(Wrapper<T>::MoveCount() == size, "wrong effect from move");
+ EXPECT_TRUE(out_last == k, "wrong return value from move");
+ }
+};
+
+template <typename T, typename Convert>
+void
+test(T trash, Convert convert)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ // count is number of output elements, plus a handful
+ // more for sake of detecting buffer overruns.
+ Sequence<T> in(n, [&](size_t k) -> T {
+ T val = convert(n ^ k);
+ return val;
+ });
+
+ const size_t outN = n + GuardSize;
+ Sequence<T> out(outN, [=](size_t) { return trash; });
+ Sequence<T> expected(outN, [=](size_t) { return trash; });
+ invoke_on_all_policies(run_copy(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), outN, n, trash);
+ invoke_on_all_policies(run_copy(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), outN, n, trash);
+ invoke_on_all_policies(run_move<T>(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), n, n, trash);
+
+ // For this test const iterator isn't suitable
+ // because const rvalue-reference call copy assignment operator
+ }
+}
+
+int
+main()
+{
+ test<int32_t>(-666, [](size_t j) { return int32_t(j); });
+ test<Wrapper<float64_t>>(Wrapper<float64_t>(-666.0), [](int32_t j) { return Wrapper<float64_t>(j); });
+
+#if !defined(_PSTL_ICC_16_17_TEST_64_TIMEOUT)
+ test<float64_t>(-666.0, [](size_t j) { return float64_t(j); });
+ test<Number>(Number(42, OddTag()), [](int32_t j) { return Number(j, OddTag()); });
+#endif
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/fill.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/fill.pass.cpp
new file mode 100644
index 00000000000000..d44a1a483c1975
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/fill.pass.cpp
@@ -0,0 +1,100 @@
+// -*- C++ -*-
+//===-- fill.pass.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_fill
+{
+ template <typename It, typename T>
+ bool
+ check(It first, It last, const T& value)
+ {
+ for (; first != last; ++first)
+ if (*first != value)
+ return false;
+ return true;
+ }
+
+ template <typename Policy, typename Iterator, typename T>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, const T& value)
+ {
+ fill(first, last, T(value + 1)); // initialize memory with different value
+
+ fill(exec, first, last, value);
+ EXPECT_TRUE(check(first, last, value), "fill wrong result");
+ }
+};
+
+struct test_fill_n
+{
+ template <typename It, typename Size, typename T>
+ bool
+ check(It first, Size n, const T& value)
+ {
+ for (Size i = 0; i < n; ++i, ++first)
+ if (*first != value)
+ return false;
+ return true;
+ }
+
+ template <typename Policy, typename Iterator, typename Size, typename T>
+ void
+ operator()(Policy&& exec, Iterator first, Size n, const T& value)
+ {
+ fill_n(first, n, T(value + 1)); // initialize memory with different value
+
+ const Iterator one_past_last = fill_n(exec, first, n, value);
+ const Iterator expected_return = std::next(first, n);
+
+ EXPECT_TRUE(expected_return == one_past_last, "fill_n should return Iterator to one past the element assigned");
+ EXPECT_TRUE(check(first, n, value), "fill_n wrong result");
+
+ //n == -1
+ const Iterator res = fill_n(exec, first, -1, value);
+ EXPECT_TRUE(res == first, "fill_n wrong result for n == -1");
+ }
+};
+
+template <typename T>
+void
+test_fill_by_type(std::size_t n)
+{
+ Sequence<T> in(n, [](std::size_t) -> T { return T(0); }); //fill with zeros
+ T value = -1;
+
+ invoke_on_all_policies(test_fill(), in.begin(), in.end(), value);
+ invoke_on_all_policies(test_fill_n(), in.begin(), n, value);
+}
+
+int
+main()
+{
+
+ const std::size_t N = 100000;
+
+ for (std::size_t n = 0; n < N; n = n < 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ test_fill_by_type<int32_t>(n);
+ test_fill_by_type<float64_t>(n);
+ }
+
+ std::cout << done() << std::endl;
+
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/generate.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/generate.pass.cpp
new file mode 100644
index 00000000000000..92d87ea26e541b
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/generate.pass.cpp
@@ -0,0 +1,104 @@
+// -*- C++ -*-
+//===-- generate.pass.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <atomic>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct Generator_count
+{
+ const T def_val = T(-1);
+ T
+ operator()()
+ {
+ return def_val;
+ }
+ T
+ default_value() const
+ {
+ return def_val;
+ }
+};
+
+struct test_generate
+{
+ template <typename Policy, typename Iterator, typename Size>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Size n)
+ {
+ using namespace std;
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+
+ // Try random-access iterator
+ {
+ Generator_count<T> g;
+ generate(exec, first, last, g);
+ Size count = std::count(first, last, g.default_value());
+ EXPECT_TRUE(count == n, "generate wrong result for generate");
+ std::fill(first, last, T(0));
+ }
+
+ {
+ Generator_count<T> g;
+ const auto m = n / 2;
+ auto actual_last = generate_n(exec, first, m, g);
+ Size count = std::count(first, actual_last, g.default_value());
+ EXPECT_TRUE(count == m && actual_last == std::next(first, m), "generate_n wrong result for generate_n");
+ std::fill(first, actual_last, T(0));
+ }
+ }
+};
+
+template <typename T>
+void
+test_generate_by_type()
+{
+ for (size_t n = 0; n <= 100000; n = n < 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, [](size_t) -> T { return T(0); }); //fill by zero
+
+ invoke_on_all_policies(test_generate(), in.begin(), in.end(), in.size());
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto gen = []() { return T(0); };
+
+ generate(exec, iter, iter, non_const(gen));
+ generate_n(exec, iter, 0, non_const(gen));
+ }
+};
+
+int
+main()
+{
+
+ test_generate_by_type<int32_t>();
+ test_generate_by_type<float64_t>();
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove.pass.cpp
new file mode 100644
index 00000000000000..872b0d292caa7a
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove.pass.cpp
@@ -0,0 +1,161 @@
+// -*- C++ -*-
+//===-- remove.pass.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Test for remove, remove_if
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct run_remove
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename Size, typename T>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator expected_first, OutputIterator expected_last, Size n,
+ const T& value)
+ {
+ }
+ template <typename InputIterator, typename OutputIterator, typename Size, typename T>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator expected_first,
+ OutputIterator expected_last, Size n, const T& value)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename Size, typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator expected_first, OutputIterator expected_last, Size,
+ const T& value)
+ {
+ // Cleaning
+ std::copy(first, last, expected_first);
+ std::copy(first, last, out_first);
+
+ // Run remove
+ OutputIterator i = remove(expected_first, expected_last, value);
+ OutputIterator k = remove(exec, out_first, out_last, value);
+ EXPECT_TRUE(std::distance(expected_first, i) == std::distance(out_first, k), "wrong return value from remove");
+ EXPECT_EQ_N(expected_first, out_first, std::distance(expected_first, i), "wrong remove effect");
+ }
+};
+
+struct run_remove_if
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename Size, typename Predicate>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator expected_first, OutputIterator expected_last, Size n,
+ Predicate pred)
+ {
+ }
+ template <typename InputIterator, typename OutputIterator, typename Size, typename Predicate>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator expected_first,
+ OutputIterator expected_last, Size n, Predicate pred)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename Size, typename Predicate>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator expected_first, OutputIterator expected_last, Size,
+ Predicate pred)
+ {
+ // Cleaning
+ std::copy(first, last, expected_first);
+ std::copy(first, last, out_first);
+
+ // Run remove_if
+ OutputIterator i = remove_if(expected_first, expected_last, pred);
+ OutputIterator k = remove_if(exec, out_first, out_last, pred);
+ EXPECT_TRUE(std::distance(expected_first, i) == std::distance(out_first, k),
+ "wrong return value from remove_if");
+ EXPECT_EQ_N(expected_first, out_first, std::distance(expected_first, i), "wrong remove_if effect");
+ }
+};
+
+template <typename T, typename Predicate, typename Convert>
+void
+test(T trash, const T& value, Predicate pred, Convert convert)
+{
+ const std::size_t max_size = 100000;
+ Sequence<T> out(max_size, [trash](size_t) { return trash; });
+ Sequence<T> expected(max_size, [trash](size_t) { return trash; });
+
+ for (size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> data(n, [&](size_t k) -> T { return convert(k); });
+
+ invoke_on_all_policies(run_remove(), data.begin(), data.end(), out.begin(), out.begin() + n, expected.begin(),
+ expected.begin() + n, n, value);
+ invoke_on_all_policies(run_remove_if(), data.begin(), data.end(), out.begin(), out.begin() + n,
+ expected.begin(), expected.begin() + n, n, pred);
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+
+ invoke_if(exec, [&]() { remove_if(exec, iter, iter, non_const(is_even)); });
+ }
+};
+
+int
+main()
+{
+#if !defined(_PSTL_ICC_18_TEST_EARLY_EXIT_MONOTONIC_RELEASE_BROKEN)
+ test<int32_t>(666, 42, [](int32_t) { return true; }, [](size_t j) { return j; });
+#endif
+
+ test<int32_t>(666, 2001, [](const int32_t& val) { return val != 2001; },
+ [](size_t j) { return ((j + 1) % 5 & 2) != 0 ? 2001 : -1 - int32_t(j); });
+ test<float64_t>(-666.0, 8.5, [](const float64_t& val) { return val != 8.5; },
+ [](size_t j) { return ((j + 1) % 7 & 2) != 0 ? 8.5 : float64_t(j % 32 + j); });
+
+#if !defined(_PSTL_ICC_17_TEST_MAC_RELEASE_32_BROKEN)
+ test<Number>(Number(-666, OddTag()), Number(42, OddTag()), IsMultiple(3, OddTag()),
+ [](int32_t j) { return Number(j, OddTag()); });
+#endif
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ test<MemoryChecker>(MemoryChecker{0}, MemoryChecker{1},
+ [](const MemoryChecker& val){ return val.value() == 1; },
+ [](std::size_t idx){ return MemoryChecker{std::int32_t(idx % 3 == 0)}; }
+ );
+ EXPECT_FALSE(MemoryChecker::alive_objects() < 0, "wrong effect from remove,remove_if: number of ctors calls < num of dtors calls");
+ EXPECT_FALSE(MemoryChecker::alive_objects() > 0, "wrong effect from remove,remove_if: number of ctors calls > num of dtors calls");
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove_copy.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove_copy.pass.cpp
new file mode 100644
index 00000000000000..9cdd7d62a4f243
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/remove_copy.pass.cpp
@@ -0,0 +1,91 @@
+// -*- C++ -*-
+//===-- remove_copy.pass.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct run_remove_copy
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2, Size n, const T& value,
+ T trash)
+ {
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+
+ // Run copy_if
+ auto i = std::remove_copy(first, last, expected_first, value);
+ (void)i;
+ auto k = std::remove_copy(exec, first, last, out_first, value);
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong remove_copy effect");
+ for (size_t j = 0; j < GuardSize; ++j)
+ {
+ ++k;
+ }
+ EXPECT_TRUE(out_last == k, "wrong return value from remove_copy");
+ }
+};
+
+template <typename T, typename Convert>
+void
+test(T trash, const T& value, Convert convert, bool check_weakness = true)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ // count is number of output elements, plus a handful
+ // more for sake of detecting buffer overruns.
+ size_t count = GuardSize;
+ Sequence<T> in(n, [&](size_t k) -> T {
+ T x = convert(n ^ k);
+ count += !(x == value) ? 1 : 0;
+ return x;
+ });
+ using namespace std;
+
+ Sequence<T> out(count, [=](size_t) { return trash; });
+ Sequence<T> expected(count, [=](size_t) { return trash; });
+ if (check_weakness)
+ {
+ auto expected_result = remove_copy(in.cfbegin(), in.cfend(), expected.begin(), value);
+ size_t m = expected_result - expected.begin();
+ EXPECT_TRUE(n / 4 <= m && m <= 3 * (n + 1) / 4, "weak test for remove_copy");
+ }
+ invoke_on_all_policies(run_remove_copy(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), count, value, trash);
+ invoke_on_all_policies(run_remove_copy(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), count, value, trash);
+ }
+}
+
+int
+main()
+{
+
+ test<float64_t>(-666.0, 8.5, [](size_t j) { return ((j + 1) % 7 & 2) != 0 ? 8.5 : float64_t(j % 32 + j); });
+
+ test<int32_t>(-666, 42, [](size_t j) { return ((j + 1) % 5 & 2) != 0 ? 42 : -1 - int32_t(j); });
+
+ test<Number>(Number(42, OddTag()), Number(2001, OddTag()),
+ [](int32_t j) { return ((j + 1) % 3 & 2) != 0 ? Number(2001, OddTag()) : Number(j, OddTag()); });
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace.pass.cpp
new file mode 100644
index 00000000000000..9c6ca1ea8c0faa
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace.pass.cpp
@@ -0,0 +1,160 @@
+// -*- C++ -*-
+//===-- replace.pass.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// This class is needed to check the self-copying
+struct copy_int
+{
+ int32_t value;
+ int32_t copied_times = 0;
+ constexpr explicit copy_int(int32_t val = 0) : value(val) {}
+ constexpr copy_int(copy_int const& other) : value(other.value), copied_times(other.copied_times) { }
+
+ constexpr copy_int&
+ operator=(const copy_int& other)
+ {
+ if (&other == this)
+ copied_times++;
+ else
+ {
+ value = other.value;
+ copied_times = other.copied_times;
+ }
+ return *this;
+ }
+
+ constexpr bool
+ operator==(const copy_int& other) const
+ {
+ return (value == other.value);
+ }
+};
+
+template <typename Iterator>
+struct test_one_policy
+{
+ std::size_t len;
+ Iterator data_b;
+ Iterator data_e;
+ test_one_policy(Iterator data_, std::size_t len_)
+ {
+ len = len_;
+ data_b = data_;
+ data_e = std::next(data_b, len);
+ }
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 expected_b, Iterator1 expected_e, Iterator2 actual_b,
+ Iterator2 actual_e, Predicate pred, const T& value, const T& old_value)
+ {
+ using namespace std;
+
+ copy(data_b, data_e, expected_b);
+ copy(data_b, data_e, actual_b);
+
+ replace(expected_b, expected_e, old_value, value);
+ replace(exec, actual_b, actual_e, old_value, value);
+
+ EXPECT_TRUE((check<T, Iterator2>(actual_b, actual_e)), "wrong result of self assignment check");
+ EXPECT_TRUE(equal(expected_b, expected_e, actual_b), "wrong result of replace");
+
+ copy(data_b, data_e, expected_b);
+ copy(data_b, data_e, actual_b);
+
+ replace_if(expected_b, expected_e, pred, value);
+ replace_if(exec, actual_b, actual_e, pred, value);
+ EXPECT_TRUE(equal(expected_b, expected_e, actual_b), "wrong result of replace_if");
+ }
+
+ template <typename T, typename Iterator1>
+ bool check(Iterator1, Iterator1)
+ {
+ return true;
+ }
+
+ template <typename T, typename Iterator1>
+ typename std::enable_if<std::is_same<T, copy_int>::value, bool>::type_t
+ check(Iterator1 b, Iterator1 e)
+ {
+ return std::all_of(b, e, [](const copy_int& elem) { return elem.copied_times == 0; });
+ }
+};
+
+template <typename T1, typename T2, typename Pred>
+void
+test(Pred pred)
+{
+ typedef typename Sequence<T2>::iterator iterator_type;
+
+ const std::size_t max_len = 100000;
+
+ static constexpr T1 value = T1(0);
+ static constexpr T1 new_value = T1(666);
+
+ Sequence<T2> expected(max_len);
+ Sequence<T2> actual(max_len);
+
+ Sequence<T2> data(max_len, [](std::size_t i) {
+ if (i % 3 == 2)
+ {
+ return T1(i);
+ }
+ else
+ {
+ return value;
+ }
+ });
+
+ for (std::size_t len = 0; len < max_len; len = len <= 16 ? len + 1 : std::size_t(3.1415 * len))
+ {
+ test_one_policy<iterator_type> temp(data.begin(), len);
+
+ invoke_on_all_policies(temp, expected.begin(), expected.begin() + len, actual.begin(), actual.begin() + len,
+ pred, new_value, value);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ invoke_if(exec, [&]() { replace_if(exec, iter, iter, non_const(is_even), T(0)); });
+ }
+};
+
+int
+main()
+{
+ test<int32_t, float32_t>(__pstl::__internal::__equal_value<int32_t>(666));
+ test<uint16_t, uint8_t>([](const uint16_t& elem) { return elem % 3 < 2; });
+ test<float64_t, int64_t>([](const float64_t& elem) { return elem * elem - 3.5 * elem > 10; });
+ test<copy_int, copy_int>([](const copy_int& val) { return val.value / 5 > 2; });
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace_copy.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace_copy.pass.cpp
new file mode 100644
index 00000000000000..94d725fb1c500c
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/replace_copy.pass.cpp
@@ -0,0 +1,105 @@
+// -*- C++ -*-
+//===-- replace_copy.pass.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for replace_copy and replace_copy_if
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_replace_copy
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2, Size n, Predicate pred,
+ const T& old_value, const T& new_value, T trash)
+ {
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+ // Run replace_copy
+ auto i = std::replace_copy(first, last, expected_first, old_value, new_value);
+ auto k = std::replace_copy(exec, first, last, out_first, old_value, new_value);
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong replace_copy effect");
+ EXPECT_TRUE(out_last == k, "wrong return value from replace_copy");
+
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+ // Run replace_copy_if
+ i = replace_copy_if(first, last, expected_first, pred, new_value);
+ k = replace_copy_if(exec, first, last, out_first, pred, new_value);
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong replace_copy_if effect");
+ EXPECT_TRUE(out_last == k, "wrong return value from replace_copy_if");
+ }
+};
+
+template <typename T, typename Convert, typename Predicate>
+void
+test(T trash, const T& old_value, const T& new_value, Predicate pred, Convert convert)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, [&](size_t k) -> T { return convert(n ^ k); });
+ Sequence<T> out(n, [=](size_t) { return trash; });
+ Sequence<T> expected(n, [=](size_t) { return trash; });
+
+ invoke_on_all_policies(test_replace_copy(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), out.size(), pred, old_value, new_value, trash);
+ invoke_on_all_policies(test_replace_copy(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), out.size(), pred, old_value, new_value, trash);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+
+ invoke_if(exec, [&]() { replace_copy_if(exec, input_iter, input_iter, out_iter, non_const(is_even), T(0)); });
+ }
+};
+
+int
+main()
+{
+
+ test<float64_t>(-666.0, 8.5, 0.33, [](const float64_t& x) { return x * x <= 1024; },
+ [](size_t j) { return ((j + 1) % 7 & 2) != 0 ? 8.5 : float64_t(j % 32 + j); });
+
+ test<int32_t>(-666, 42, 99, [](const int32_t& x) { return x != 42; },
+ [](size_t j) { return ((j + 1) % 5 & 2) != 0 ? 42 : -1 - int32_t(j); });
+
+#if !defined(_PSTL_ICC_17_TEST_MAC_RELEASE_32_BROKEN)
+ test<Number>(Number(42, OddTag()), Number(2001, OddTag()), Number(2017, OddTag()), IsMultiple(3, OddTag()),
+ [](int32_t j) { return ((j + 1) % 3 & 2) != 0 ? Number(2001, OddTag()) : Number(j, OddTag()); });
+#endif
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate.pass.cpp
new file mode 100644
index 00000000000000..0d1cfeb4ae8e37
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate.pass.cpp
@@ -0,0 +1,176 @@
+// -*- C++ -*-
+//===-- rotate.pass.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iterator>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct wrapper
+{
+ T t;
+ int move_count;
+ explicit wrapper(T t_) : t(t_), move_count(0) {}
+ wrapper&
+ operator=(const T& t_)
+ {
+ t = t_;
+ return *this;
+ }
+
+ wrapper(const wrapper<T>& a) : move_count(0) { t = a.t; }
+
+ wrapper<T>&
+ operator=(wrapper<T>& a)
+ {
+ t = a.t;
+ return *this;
+ }
+
+ wrapper<T>&
+ operator=(wrapper<T>&& a)
+ {
+ t = a.t;
+ move_count += 1;
+ return *this;
+ }
+};
+
+template <typename T>
+struct compare
+{
+ bool
+ operator()(const T& a, const T& b)
+ {
+ return a == b;
+ }
+};
+
+template <typename T>
+struct compare<wrapper<T>>
+{
+ bool
+ operator()(const wrapper<T>& a, const wrapper<T>& b)
+ {
+ return a.t == b.t;
+ }
+};
+#include <typeinfo>
+
+struct test_one_policy
+{
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specializations to skip testing in case of broken configuration
+ template <typename Iterator, typename Size>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator data_b, Iterator data_e, Iterator actual_b,
+ Iterator actual_e, Size shift)
+ {
+ }
+ template <typename Iterator, typename Size>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator data_b, Iterator data_e, Iterator actual_b,
+ Iterator actual_e, Size shift)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator, typename Size>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator data_b, Iterator data_e, Iterator actual_b, Iterator actual_e,
+ Size shift)
+ {
+ using namespace std;
+ using T = typename iterator_traits<Iterator>::value_type;
+ Iterator actual_m = std::next(actual_b, shift);
+
+ copy(data_b, data_e, actual_b);
+ Iterator actual_return = rotate(exec, actual_b, actual_m, actual_e);
+
+ EXPECT_TRUE(actual_return == std::next(actual_b, std::distance(actual_m, actual_e)), "wrong result of rotate");
+ auto comparator = compare<T>();
+ bool check = std::equal(actual_return, actual_e, data_b, comparator);
+ check = check && std::equal(actual_b, actual_return, std::next(data_b, shift), comparator);
+
+ EXPECT_TRUE(check, "wrong effect of rotate");
+ EXPECT_TRUE(check_move(exec, actual_b, actual_e, shift), "wrong move test of rotate");
+ }
+
+ template <typename ExecutionPolicy, typename Iterator, typename Size>
+ typename std::enable_if<
+ is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
+ !std::is_same<ExecutionPolicy, std::execution::sequenced_policy>::value &&
+ std::is_same<typename std::iterator_traits<Iterator>::value_type, wrapper<float32_t>>::value,
+ bool>::type
+ check_move(ExecutionPolicy&&, Iterator b, Iterator e, Size shift)
+ {
+ bool result = all_of(b, e, [](wrapper<float32_t>& a) {
+ bool temp = a.move_count > 0;
+ a.move_count = 0;
+ return temp;
+ });
+ return shift == 0 || result;
+ }
+
+ template <typename ExecutionPolicy, typename Iterator, typename Size>
+ typename std::enable_if<
+ !(is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value &&
+ !std::is_same<ExecutionPolicy, std::execution::sequenced_policy>::value &&
+ std::is_same<typename std::iterator_traits<Iterator>::value_type, wrapper<float32_t>>::value),
+ bool>::type
+ check_move(ExecutionPolicy&&, Iterator, Iterator, Size)
+ {
+ return true;
+ }
+};
+
+template <typename T>
+void
+test()
+{
+ const int32_t max_len = 100000;
+
+ Sequence<T> actual(max_len, [](std::size_t i) { return T(i); });
+ Sequence<T> data(max_len, [](std::size_t i) { return T(i); });
+
+ for (int32_t len = 0; len < max_len; len = len <= 16 ? len + 1 : int32_t(3.1415 * len))
+ {
+ int32_t shifts[] = {0, 1, 2, len / 3, (2 * len) / 3, len - 1};
+ for (auto shift : shifts)
+ {
+ if (shift >= 0 && shift < len)
+ {
+ invoke_on_all_policies(test_one_policy(), data.begin(), data.begin() + len, actual.begin(),
+ actual.begin() + len, shift);
+ }
+ }
+ }
+}
+
+int
+main()
+{
+ test<int32_t>();
+ test<wrapper<float64_t>>();
+ test<MemoryChecker>();
+ EXPECT_FALSE(MemoryChecker::alive_objects() < 0, "wrong effect from rotate: number of ctors calls < num of dtors calls");
+ EXPECT_FALSE(MemoryChecker::alive_objects() > 0, "wrong effect from rotate: number of ctors calls > num of dtors calls");
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate_copy.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate_copy.pass.cpp
new file mode 100644
index 00000000000000..539cefc929d452
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/rotate_copy.pass.cpp
@@ -0,0 +1,146 @@
+// -*- C++ -*-
+//===-- rotate_copy.pass.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iterator>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct wrapper;
+
+template <typename T>
+bool
+compare(const wrapper<T>& a, const wrapper<T>& b)
+{
+ return a.t == b.t;
+}
+
+template <typename T>
+bool
+compare(const T& a, const T& b)
+{
+ return a == b;
+}
+
+template <typename T>
+struct wrapper
+{
+ explicit wrapper(T t_) : t(t_) {}
+ wrapper&
+ operator=(const T& t_)
+ {
+ t = t_;
+ return *this;
+ }
+ friend bool
+ compare<T>(const wrapper<T>& a, const wrapper<T>& b);
+
+ private:
+ T t;
+};
+
+template <typename T, typename It1, typename It2>
+struct comparator
+{
+ using T1 = typename std::iterator_traits<It1>::value_type;
+ using T2 = typename std::iterator_traits<It2>::value_type;
+ bool
+ operator()(T1 a, T2 b)
+ {
+ T temp = a;
+ return compare(temp, b);
+ }
+};
+
+struct test_one_policy
+{
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1, typename Iterator2>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::unsequenced_policy, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b,
+ Iterator2 actual_e, std::size_t shift)
+ {
+ }
+ template <typename Iterator1, typename Iterator2>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b,
+ Iterator2 actual_e, std::size_t shift)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b, Iterator2 actual_e,
+ std::size_t shift)
+ {
+ using namespace std;
+ using T = typename iterator_traits<Iterator2>::value_type;
+ Iterator1 data_m = std::next(data_b, shift);
+
+ fill(actual_b, actual_e, T(-123));
+ Iterator2 actual_return = rotate_copy(exec, data_b, data_m, data_e, actual_b);
+
+ EXPECT_TRUE(actual_return == actual_e, "wrong result of rotate_copy");
+ auto comparer = comparator<T, Iterator1, Iterator2>();
+ bool check = std::equal(data_m, data_e, actual_b, comparer);
+ check = check && std::equal(data_b, data_m, std::next(actual_b, std::distance(data_m, data_e)), comparer);
+
+ EXPECT_TRUE(check, "wrong effect of rotate_copy");
+ }
+};
+
+template <typename T1, typename T2>
+void
+test()
+{
+
+ const std::size_t max_len = 100000;
+
+ Sequence<T2> actual(max_len, [](std::size_t i) { return T1(i); });
+
+ Sequence<T1> data(max_len, [](std::size_t i) { return T1(i); });
+
+ for (std::size_t len = 0; len < max_len; len = len <= 16 ? len + 1 : std::size_t(3.1415 * len))
+ {
+ std::size_t shifts[] = {0, 1, 2, len / 3, (2 * len) / 3, len - 1};
+ for (std::size_t shift : shifts)
+ {
+ if (shift > 0 && shift < len)
+ {
+ invoke_on_all_policies(test_one_policy(), data.begin(), data.begin() + len, actual.begin(),
+ actual.begin() + len, shift);
+ invoke_on_all_policies(test_one_policy(), data.cbegin(), data.cbegin() + len, actual.begin(),
+ actual.begin() + len, shift);
+ }
+ }
+ }
+}
+
+int
+main()
+{
+ test<int32_t, int8_t>();
+ test<uint16_t, float32_t>();
+ test<float64_t, int64_t>();
+ test<wrapper<float64_t>, wrapper<float64_t>>();
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/swap_ranges.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/swap_ranges.pass.cpp
new file mode 100644
index 00000000000000..c3c3693af59426
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/swap_ranges.pass.cpp
@@ -0,0 +1,133 @@
+// -*- C++ -*-
+//===-- swap_ranges.pass.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iterator>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct wrapper
+{
+ T t;
+ std::size_t number_of_swaps = 0;
+ wrapper() {}
+ explicit wrapper(T t_) : t(t_) {}
+ template <typename U>
+ void
+ operator=(const U& b)
+ {
+ t = b;
+ }
+ bool
+ operator==(const wrapper<T>& a) const
+ {
+ return t == a.t;
+ }
+};
+
+template <typename T>
+void
+swap(wrapper<T>& a, wrapper<T>& b)
+{
+ std::swap(a.t, b.t);
+ a.number_of_swaps++;
+ b.number_of_swaps++;
+}
+
+template <typename T>
+struct check_swap
+{
+ bool
+ operator()(T&)
+ {
+ return true;
+ }
+};
+
+template <typename T>
+struct check_swap<wrapper<T>>
+{
+ bool
+ operator()(wrapper<T>& a)
+ {
+ bool temp = (a.number_of_swaps == 1);
+ a.number_of_swaps = 0;
+ return temp;
+ }
+};
+
+struct test_one_policy
+{
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b, Iterator2 actual_e)
+ {
+ using namespace std;
+ using T_ref = typename iterator_traits<Iterator1>::reference;
+ using T = typename iterator_traits<Iterator1>::value_type;
+
+ iota(data_b, data_e, 0);
+ iota(actual_b, actual_e, std::distance(data_b, data_e));
+
+ Iterator2 actual_return = swap_ranges(exec, data_b, data_e, actual_b);
+ bool check_return = (actual_return == actual_e);
+ EXPECT_TRUE(check_return, "wrong result of swap_ranges");
+ if (check_return)
+ {
+ std::size_t i = 0;
+ bool check = all_of(actual_b, actual_e, [&i](T_ref a) { return a == T(i++); }) &&
+ all_of(data_b, data_e, [&i](T_ref a) { return a == T(i++); });
+
+ EXPECT_TRUE(check, "wrong effect of swap_ranges");
+
+ if (check)
+ {
+ bool swap_check =
+ all_of(data_b, data_e, check_swap<T>()) && all_of(actual_b, actual_e, check_swap<T>());
+ EXPECT_TRUE(swap_check, "wrong effect of swap_ranges swap check");
+ }
+ }
+ }
+};
+
+template <typename T>
+void
+test()
+{
+ const std::size_t max_len = 100000;
+
+ Sequence<T> data(max_len);
+ Sequence<T> actual(max_len);
+
+ for (std::size_t len = 0; len < max_len; len = len <= 16 ? len + 1 : std::size_t(3.1415 * len))
+ {
+ invoke_on_all_policies(test_one_policy(), data.begin(), data.begin() + len, actual.begin(),
+ actual.begin() + len);
+ }
+}
+
+int
+main()
+{
+ test<wrapper<uint16_t>>();
+ test<wrapper<float64_t>>();
+ test<int32_t>();
+ test<float32_t>();
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_binary.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_binary.pass.cpp
new file mode 100644
index 00000000000000..949e6f54ffb413
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_binary.pass.cpp
@@ -0,0 +1,122 @@
+// -*- C++ -*-
+//===-- transform_binary.pass.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename In1, typename In2, typename Out>
+class TheOperation
+{
+ Out val;
+
+ public:
+ TheOperation(Out v) : val(v) {}
+ Out
+ operator()(const In1& x, const In2& y) const
+ {
+ return Out(val + x - y);
+ }
+};
+
+template <typename InputIterator1, typename InputIterator2, typename OutputIterator>
+void
+check_and_reset(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator out_first)
+{
+ typedef typename std::iterator_traits<OutputIterator>::value_type Out;
+ typename std::iterator_traits<OutputIterator>::difference_type k = 0;
+ for (; first1 != last1; ++first1, ++first2, ++out_first, ++k)
+ {
+ // check
+ Out expected = Out(1.5) + *first1 - *first2;
+ Out actual = *out_first;
+ if (std::is_floating_point<Out>::value)
+ {
+ EXPECT_TRUE((expected > actual ? expected - actual : actual - expected) < 1e7,
+ "wrong value in output sequence");
+ }
+ else
+ {
+ EXPECT_EQ(expected, actual, "wrong value in output sequence");
+ }
+ // reset
+ *out_first = k % 7 != 4 ? 7 * k - 5 : 0;
+ }
+}
+
+struct test_one_policy
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename OutputIterator,
+ typename BinaryOp>
+ void
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2,
+ OutputIterator out_first, OutputIterator, BinaryOp op)
+ {
+ auto result = std::transform(exec, first1, last1, first2, out_first, op);
+ (void)result;
+ check_and_reset(first1, last1, first2, out_first);
+ }
+};
+
+template <typename In1, typename In2, typename Out, typename Predicate>
+void
+test(Predicate pred)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<In1> in1(n, [](size_t k) { return k % 5 != 1 ? 3 * k - 7 : 0; });
+ Sequence<In2> in2(n, [](size_t k) { return k % 7 != 2 ? 5 * k - 5 : 0; });
+
+ Sequence<Out> out(n, [](size_t) { return -1; });
+
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.end(), in2.begin(), in2.end(), out.begin(),
+ out.end(), pred);
+ invoke_on_all_policies(test_one_policy(), in1.cbegin(), in1.cend(), in2.cbegin(), in2.cend(), out.begin(),
+ out.end(), pred);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ invoke_if(exec, [&]() {
+ InputIterator input_iter2 = input_iter;
+ transform(exec, input_iter, input_iter, input_iter2, out_iter, non_const(std::plus<T>()));
+ });
+ }
+};
+
+int
+main()
+{
+ //const operator()
+ test<int32_t, int32_t, int32_t>(TheOperation<int32_t, int32_t, int32_t>(1));
+ test<float32_t, float32_t, float32_t>(TheOperation<float32_t, float32_t, float32_t>(1.5));
+ //non-const operator()
+ test<int32_t, float32_t, float32_t>(non_const(TheOperation<int32_t, float32_t, float32_t>(1.5)));
+ test<int64_t, float64_t, float32_t>(non_const(TheOperation<int64_t, float64_t, float32_t>(1.5)));
+ //lambda
+ test<int8_t, float64_t, int8_t>([](const int8_t& x, const float64_t& y) { return int8_t(int8_t(1.5) + x - y); });
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_unary.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_unary.pass.cpp
new file mode 100644
index 00000000000000..69a410c4cd6eb6
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/transform_unary.pass.cpp
@@ -0,0 +1,91 @@
+// -*- C++ -*-
+//===-- transform_unary.pass.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename InputIterator, typename OutputIterator>
+void
+check_and_reset(InputIterator first, InputIterator last, OutputIterator out_first)
+{
+ typedef typename std::iterator_traits<OutputIterator>::value_type Out;
+ typename std::iterator_traits<OutputIterator>::difference_type k = 0;
+ for (; first != last; ++first, ++out_first, ++k)
+ {
+ // check
+ Out expected = 1 - *first;
+ Out actual = *out_first;
+ EXPECT_EQ(expected, actual, "wrong value in output sequence");
+ // reset
+ *out_first = k % 7 != 4 ? 7 * k - 5 : 0;
+ }
+}
+
+struct test_one_policy
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename UnaryOp>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, UnaryOp op)
+ {
+ auto orr = std::transform(exec, first, last, out_first, op);
+ EXPECT_TRUE(out_last == orr, "transform returned wrong iterator");
+ check_and_reset(first, last, out_first);
+ }
+};
+
+template <typename Tin, typename Tout>
+void
+test()
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<Tin> in(n, [](int32_t k) { return k % 5 != 1 ? 3 * k - 7 : 0; });
+
+ Sequence<Tout> out(n);
+
+ const auto flip = Complement<Tin, Tout>(1);
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.end(), out.begin(), out.end(), flip);
+ invoke_on_all_policies(test_one_policy(), in.cbegin(), in.cend(), out.begin(), out.end(), flip);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ invoke_if(exec, [&]() { transform(exec, input_iter, input_iter, out_iter, non_const(std::negate<T>())); });
+ }
+};
+
+int
+main()
+{
+ test<int32_t, int32_t>();
+ test<int32_t, float32_t>();
+ test<uint16_t, float32_t>();
+ test<float32_t, float64_t>();
+ test<float64_t, float64_t>();
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique.pass.cpp
new file mode 100644
index 00000000000000..fbd4742b7e4572
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique.pass.cpp
@@ -0,0 +1,163 @@
+// -*- C++ -*-
+//===-- unique.pass.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Test for unique
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct run_unique
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename ForwardIt, typename Generator>
+ void
+ operator()(pstl::execution::unsequenced_policy, ForwardIt first1, ForwardIt last1, ForwardIt first2,
+ ForwardIt last2, Generator generator)
+ {
+ }
+
+ template <typename ForwardIt, typename Generator>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, ForwardIt first1, ForwardIt last1, ForwardIt first2,
+ ForwardIt last2, Generator generator)
+ {
+ }
+
+ template <typename ForwardIt, typename BinaryPred, typename Generator>
+ void
+ operator()(pstl::execution::unsequenced_policy, ForwardIt first1, ForwardIt last1, ForwardIt first2,
+ ForwardIt last2, BinaryPred pred, Generator generator)
+ {
+ }
+
+ template <typename ForwardIt, typename BinaryPred, typename Generator>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, ForwardIt first1, ForwardIt last1, ForwardIt first2,
+ ForwardIt last2, BinaryPred pred, Generator generator)
+ {
+ }
+#endif
+
+ template <typename Policy, typename ForwardIt, typename Generator>
+ void
+ operator()(Policy&& exec, ForwardIt first1, ForwardIt last1, ForwardIt first2, ForwardIt last2, Generator generator)
+ {
+ using namespace std;
+
+ // Preparation
+ fill_data(first1, last1, generator);
+ fill_data(first2, last2, generator);
+
+ ForwardIt i = unique(first1, last1);
+ ForwardIt k = unique(exec, first2, last2);
+
+ auto n = std::distance(first1, i);
+ EXPECT_TRUE(std::distance(first2, k) == n, "wrong return value from unique without predicate");
+ EXPECT_EQ_N(first1, first2, n, "wrong effect from unique without predicate");
+ }
+
+ template <typename Policy, typename ForwardIt, typename BinaryPred, typename Generator>
+ void
+ operator()(Policy&& exec, ForwardIt first1, ForwardIt last1, ForwardIt first2, ForwardIt last2, BinaryPred pred,
+ Generator generator)
+ {
+ using namespace std;
+
+ // Preparation
+ fill_data(first1, last1, generator);
+ fill_data(first2, last2, generator);
+
+ ForwardIt i = unique(first1, last1, pred);
+ ForwardIt k = unique(exec, first2, last2, pred);
+
+ auto n = std::distance(first1, i);
+ EXPECT_TRUE(std::distance(first2, k) == n, "wrong return value from unique with predicate");
+ EXPECT_EQ_N(first1, first2, n, "wrong effect from unique with predicate");
+ }
+};
+
+template <typename T, typename Generator, typename Predicate>
+void
+test(Generator generator, Predicate pred)
+{
+ const std::size_t max_size = 1000000;
+ Sequence<T> in(max_size, [](size_t v) { return T(v); });
+ Sequence<T> exp(max_size, [](size_t v) { return T(v); });
+
+ for (size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ invoke_on_all_policies(run_unique(), exp.begin(), exp.begin() + n, in.begin(), in.begin() + n, generator);
+ invoke_on_all_policies(run_unique(), exp.begin(), exp.begin() + n, in.begin(), in.begin() + n, pred, generator);
+ }
+}
+
+template <typename T>
+struct LocalWrapper
+{
+ T my_val;
+
+ explicit LocalWrapper(T k) : my_val(k) {}
+ LocalWrapper(LocalWrapper&& input) : my_val(std::move(input.my_val)) {}
+ LocalWrapper&
+ operator=(LocalWrapper&& input)
+ {
+ my_val = std::move(input.my_val);
+ return *this;
+ }
+ friend bool
+ operator==(const LocalWrapper<T>& x, const LocalWrapper<T>& y)
+ {
+ return x.my_val == y.my_val;
+ }
+};
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ invoke_if(exec, [&]() { unique(exec, iter, iter, non_const(std::equal_to<T>())); });
+ }
+};
+
+int
+main()
+{
+#if !defined(_PSTL_ICC_16_17_18_TEST_UNIQUE_MASK_RELEASE_BROKEN)
+ test<int32_t>([](size_t j) { return j / 3; },
+ [](const int32_t& val1, const int32_t& val2) { return val1 * val1 == val2 * val2; });
+ test<float64_t>([](size_t) { return float64_t(1); },
+ [](const float64_t& val1, const float64_t& val2) { return val1 != val2; });
+#endif
+ test<LocalWrapper<uint32_t>>([](size_t j) { return LocalWrapper<uint32_t>(j); },
+ [](const LocalWrapper<uint32_t>& val1, const LocalWrapper<uint32_t>& val2) {
+ return val1.my_val != val2.my_val;
+ });
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ test<MemoryChecker>(
+ [](std::size_t idx){ return MemoryChecker{std::int32_t(idx / 3)}; },
+ [](const MemoryChecker& val1, const MemoryChecker& val2){ return val1.value() == val2.value(); });
+ EXPECT_FALSE(MemoryChecker::alive_objects() < 0, "wrong effect from unique: number of ctors calls < num of dtors calls");
+ EXPECT_FALSE(MemoryChecker::alive_objects() > 0, "wrong effect from unique: number of ctors calls > num of dtors calls");
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique_copy_equal.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique_copy_equal.pass.cpp
new file mode 100644
index 00000000000000..356d4d24aba674
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.modifying.operations/unique_copy_equal.pass.cpp
@@ -0,0 +1,135 @@
+// -*- C++ -*-
+//===-- unique_copy_equal.pass.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for unique_copy
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct run_unique_copy
+{
+#if defined(_PSTL_ICC_16_VC14_TEST_PAR_TBB_RT_RELEASE_64_BROKEN) // dummy specializations to skip testing in case of broken configuration
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(pstl::execution::parallel_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2 expected_last, Size n,
+ Predicate pred, T trash)
+ {
+ }
+
+ template <typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, OutputIterator out_last, OutputIterator2 expected_first,
+ OutputIterator2 expected_last, Size n, Predicate pred, T trash)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Predicate, typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator2 expected_first, OutputIterator2, Size n, Predicate pred,
+ T trash)
+ {
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+
+ // Run unique_copy
+ auto i = unique_copy(first, last, expected_first);
+ auto k = unique_copy(exec, first, last, out_first);
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong unique_copy effect");
+ for (size_t j = 0; j < GuardSize; ++j)
+ {
+ ++k;
+ }
+ EXPECT_TRUE(out_last == k, "wrong return value from unique_copy");
+
+ // Cleaning
+ std::fill_n(expected_first, n, trash);
+ std::fill_n(out_first, n, trash);
+ // Run unique_copy with predicate
+ i = unique_copy(first, last, expected_first, pred);
+ k = unique_copy(exec, first, last, out_first, pred);
+ EXPECT_EQ_N(expected_first, out_first, n, "wrong unique_copy with predicate effect");
+ for (size_t j = 0; j < GuardSize; ++j)
+ {
+ ++k;
+ }
+ EXPECT_TRUE(out_last == k, "wrong return value from unique_copy with predicate");
+ }
+};
+
+template <typename T, typename BinaryPredicate, typename Convert>
+void
+test(T trash, BinaryPredicate pred, Convert convert, bool check_weakness = true)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ // count is number of output elements, plus a handful
+ // more for sake of detecting buffer overruns.
+ Sequence<T> in(n, [&](size_t k) -> T { return convert(k ^ n); });
+ using namespace std;
+ size_t count = GuardSize;
+ for (size_t k = 0; k < in.size(); ++k)
+ count += k == 0 || !pred(in[k], in[k - 1]) ? 1 : 0;
+ Sequence<T> out(count, [=](size_t) { return trash; });
+ Sequence<T> expected(count, [=](size_t) { return trash; });
+ if (check_weakness)
+ {
+ auto expected_result = unique_copy(in.begin(), in.end(), expected.begin(), pred);
+ size_t m = expected_result - expected.begin();
+ EXPECT_TRUE(n / (n < 10000 ? 4 : 6) <= m && m <= (3 * n + 1) / 4, "weak test for unique_copy");
+ }
+ invoke_on_all_policies(run_unique_copy(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), count, pred, trash);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ unique_copy(exec, input_iter, input_iter, out_iter, non_const(std::equal_to<T>()));
+ }
+};
+
+int
+main()
+{
+ test<Number>(Number(42, OddTag()), std::equal_to<Number>(),
+ [](int32_t j) { return Number(3 * j / 13 ^ (j & 8), OddTag()); });
+
+ test<float32_t>(float32_t(42), std::equal_to<float32_t>(),
+ [](int32_t j) { return float32_t(5 * j / 23 ^ (j / 7)); });
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_RELEASE_BROKEN)
+ test<float32_t>(float32_t(42), [](float32_t, float32_t) { return false; }, [](int32_t j) { return float32_t(j); },
+ false);
+#endif
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/adjacent_find.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/adjacent_find.pass.cpp
new file mode 100644
index 00000000000000..36a623bac42a4b
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/adjacent_find.pass.cpp
@@ -0,0 +1,114 @@
+// -*- C++ -*-
+//===-- adjacent_find.pass.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_adjacent_find
+{
+ template <typename Policy, typename Iterator, typename Pred>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Pred pred)
+ {
+ using namespace std;
+
+ auto k = std::adjacent_find(first, last, pred);
+ auto i = adjacent_find(exec, first, last, pred);
+ EXPECT_TRUE(i == k, "wrong return value from adjacent_find with predicate");
+
+ i = adjacent_find(exec, first, last);
+ EXPECT_TRUE(i == k, "wrong return value from adjacent_find without predicate");
+ }
+};
+
+template <typename T>
+void
+test_adjacent_find_by_type()
+{
+
+ size_t counts[] = {2, 3, 500};
+ for (size_t c = 0; c < const_size(counts); ++c)
+ {
+
+ for (size_t e = 0; e < (counts[c] >= 64 ? 64 : (counts[c] == 2 ? 1 : 2)); ++e)
+ {
+ Sequence<T> in(counts[c], [](size_t v) -> T { return T(v); }); //fill 0...n
+ in[e] = in[e + 1] = -1; //make an adjacent pair
+
+ auto i = std::adjacent_find(in.cbegin(), in.cend(), std::equal_to<T>());
+ EXPECT_TRUE(i == in.cbegin() + e, "std::adjacent_find returned wrong result");
+
+ invoke_on_all_policies(test_adjacent_find(), in.begin(), in.end(), std::equal_to<T>());
+ invoke_on_all_policies(test_adjacent_find(), in.cbegin(), in.cend(), std::equal_to<T>());
+ }
+ }
+
+ //special cases: size=0, size=1;
+ for (size_t expect = 0; expect < 1; ++expect)
+ {
+ Sequence<T> in(expect, [](size_t v) -> T { return T(v); }); //fill 0...n
+ auto i = std::adjacent_find(in.cbegin(), in.cend(), std::equal_to<T>());
+ EXPECT_TRUE(i == in.cbegin() + expect, "std::adjacent_find returned wrong result");
+
+ invoke_on_all_policies(test_adjacent_find(), in.begin(), in.end(), std::equal_to<T>());
+ invoke_on_all_policies(test_adjacent_find(), in.cbegin(), in.cend(), std::equal_to<T>());
+ }
+
+ //special cases:
+ Sequence<T> a1 = {5, 5, 5, 6, 7, 8, 9};
+ invoke_on_all_policies(test_adjacent_find(), a1.begin(), a1.end(), std::equal_to<T>());
+ invoke_on_all_policies(test_adjacent_find(), a1.begin() + 1, a1.end(), std::equal_to<T>());
+
+ invoke_on_all_policies(test_adjacent_find(), a1.cbegin(), a1.cend(), std::equal_to<T>());
+ invoke_on_all_policies(test_adjacent_find(), a1.cbegin() + 1, a1.cend(), std::equal_to<T>());
+
+ Sequence<T> a2 = {5, 6, 7, 8, 9, 9};
+ invoke_on_all_policies(test_adjacent_find(), a2.begin(), a2.end(), std::equal_to<T>());
+ invoke_on_all_policies(test_adjacent_find(), a2.begin(), a2.end() - 1, std::equal_to<T>());
+
+ invoke_on_all_policies(test_adjacent_find(), a2.cbegin(), a2.cend(), std::equal_to<T>());
+ invoke_on_all_policies(test_adjacent_find(), a2.cbegin(), a2.cend() - 1, std::equal_to<T>());
+
+ Sequence<T> a3 = {5, 6, 6, 6, 7, 9, 9, 9, 9};
+ invoke_on_all_policies(test_adjacent_find(), a3.begin(), a3.end(), std::equal_to<T>());
+
+ invoke_on_all_policies(test_adjacent_find(), a3.cbegin(), a3.cend(), std::equal_to<T>());
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ adjacent_find(exec, iter, iter, non_const(std::equal_to<T>()));
+ }
+};
+
+int
+main()
+{
+
+ test_adjacent_find_by_type<int32_t>();
+ test_adjacent_find_by_type<float64_t>();
+
+ test_algo_basic_single<int32_t>(run_for_rnd_bi<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/all_of.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/all_of.pass.cpp
new file mode 100644
index 00000000000000..ec23dc47e2f940
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/all_of.pass.cpp
@@ -0,0 +1,117 @@
+// -*- C++ -*-
+//===-- all_of.pass.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+/*
+ TODO: consider implementing the following tests for a better code coverage
+ - correctness
+ - bad input argument (if applicable)
+ - data corruption around/of input and output
+ - correctly work with nested parallelism
+ - check that algorithm does not require anything more than is described in its requirements section
+*/
+
+using namespace TestUtils;
+
+struct test_all_of
+{
+ template <typename ExecutionPolicy, typename Iterator, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator begin, Iterator end, Predicate pred, bool expected)
+ {
+
+ auto actualr = std::all_of(exec, begin, end, pred);
+ EXPECT_EQ(expected, actualr, "result for all_of");
+ }
+};
+
+template <typename T>
+struct Parity
+{
+ bool parity;
+
+ public:
+ Parity(bool parity_) : parity(parity_) {}
+ bool
+ operator()(T value) const
+ {
+ return (size_t(value) ^ parity) % 2 == 0;
+ }
+};
+
+template <typename T>
+void
+test(size_t bits)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+
+ // Sequence of odd values
+ Sequence<T> in(n, [n, bits](size_t) { return T(2 * HashBits(n, bits - 1) ^ 1); });
+
+ // Even value, or false when T is bool.
+ T spike(2 * HashBits(n, bits - 1));
+ Sequence<T> inCopy(in);
+
+ invoke_on_all_policies(test_all_of(), in.begin(), in.end(), Parity<T>(1), true);
+ invoke_on_all_policies(test_all_of(), in.cbegin(), in.cend(), Parity<T>(1), true);
+ EXPECT_EQ(in, inCopy, "all_of modified input sequence");
+ if (n > 0)
+ {
+ // Sprinkle in a miss
+ in[2 * n / 3] = spike;
+ invoke_on_all_policies(test_all_of(), in.begin(), in.end(), Parity<T>(1), false);
+ invoke_on_all_policies(test_all_of(), in.cbegin(), in.cend(), Parity<T>(1), false);
+
+ // Sprinkle in a few more misses
+ in[n / 2] = spike;
+ in[n / 3] = spike;
+ invoke_on_all_policies(test_all_of(), in.begin(), in.end(), Parity<T>(1), false);
+ invoke_on_all_policies(test_all_of(), in.cbegin(), in.cend(), Parity<T>(1), false);
+ }
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ all_of(exec, iter, iter, non_const(is_even));
+ }
+};
+
+int
+main()
+{
+ test<int32_t>(8 * sizeof(int32_t));
+ test<uint16_t>(8 * sizeof(uint16_t));
+ test<float64_t>(53);
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_BOOL_TYPE_RELEASE_64_BROKEN)
+ test<bool>(1);
+#endif
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/any_of.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/any_of.pass.cpp
new file mode 100644
index 00000000000000..97d1691fa34ee1
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/any_of.pass.cpp
@@ -0,0 +1,103 @@
+// -*- C++ -*-
+//===-- any_of.pass.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+/*
+ TODO: consider implementing the following tests for a better code coverage
+ - correctness
+ - bad input argument (if applicable)
+ - data corruption around/of input and output
+ - correctly work with nested parallelism
+ - check that algorithm does not require anything more than is described in its requirements section
+*/
+
+using namespace TestUtils;
+
+struct test_any_of
+{
+ template <typename ExecutionPolicy, typename Iterator, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator begin, Iterator end, Predicate pred, bool expected)
+ {
+
+ auto actualr = std::any_of(exec, begin, end, pred);
+ EXPECT_EQ(expected, actualr, "result for any_of");
+ }
+};
+
+template <typename T>
+void
+test(size_t bits)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+
+ // Sequence of odd values
+ Sequence<T> in(n, [n, bits](size_t) { return T(2 * HashBits(n, bits - 1) ^ 1); });
+
+ // Even value, or false when T is bool.
+ T spike(2 * HashBits(n, bits - 1));
+ Sequence<T> inCopy(in);
+
+ invoke_on_all_policies(test_any_of(), in.begin(), in.end(), is_equal_to<T>(spike), false);
+ invoke_on_all_policies(test_any_of(), in.cbegin(), in.cend(), is_equal_to<T>(spike), false);
+ EXPECT_EQ(in, inCopy, "any_of modified input sequence");
+ if (n > 0)
+ {
+ // Sprinkle in a hit
+ in[2 * n / 3] = spike;
+ invoke_on_all_policies(test_any_of(), in.begin(), in.end(), is_equal_to<T>(spike), true);
+ invoke_on_all_policies(test_any_of(), in.cbegin(), in.cend(), is_equal_to<T>(spike), true);
+
+ // Sprinkle in a few more hits
+ in[n / 2] = spike;
+ in[n / 3] = spike;
+ invoke_on_all_policies(test_any_of(), in.begin(), in.end(), is_equal_to<T>(spike), true);
+ invoke_on_all_policies(test_any_of(), in.cbegin(), in.cend(), is_equal_to<T>(spike), true);
+ }
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ any_of(exec, iter, iter, non_const(is_even));
+ }
+};
+
+int
+main()
+{
+ test<int32_t>(8 * sizeof(int32_t));
+ test<uint16_t>(8 * sizeof(uint16_t));
+ test<float64_t>(53);
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_BOOL_TYPE_RELEASE_64_BROKEN)
+ test<bool>(1);
+#endif
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/count.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/count.pass.cpp
new file mode 100644
index 00000000000000..e8eca023d15aec
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/count.pass.cpp
@@ -0,0 +1,108 @@
+// -*- C++ -*-
+//===-- count.pass.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for count and count_if
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_count
+{
+ template <typename Policy, typename Iterator, typename T>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, T needle)
+ {
+ auto expected = std::count(first, last, needle);
+ auto result = std::count(exec, first, last, needle);
+ EXPECT_EQ(expected, result, "wrong count result");
+ }
+};
+
+struct test_count_if
+{
+ template <typename Policy, typename Iterator, typename Predicate>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Predicate pred)
+ {
+ auto expected = std::count_if(first, last, pred);
+ auto result = std::count_if(exec, first, last, pred);
+ EXPECT_EQ(expected, result, "wrong count_if result");
+ }
+};
+
+template <typename T>
+class IsEqual
+{
+ T value;
+
+ public:
+ IsEqual(T value_, OddTag) : value(value_) {}
+ bool
+ operator()(const T& x) const
+ {
+ return x == value;
+ }
+};
+
+template <typename In, typename T, typename Predicate, typename Convert>
+void
+test(T needle, Predicate pred, Convert convert)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<In> in(n, [=](size_t k) -> In {
+ // Sprinkle "42" and "50" early, so that short sequences have non-zero count.
+ return convert((n - k - 1) % 3 == 0 ? 42 : (n - k - 2) % 5 == 0 ? 50 : 3 * (int(k) % 1000 - 500));
+ });
+ invoke_on_all_policies(test_count(), in.begin(), in.end(), needle);
+ invoke_on_all_policies(test_count_if(), in.begin(), in.end(), pred);
+
+ invoke_on_all_policies(test_count(), in.cbegin(), in.cend(), needle);
+ invoke_on_all_policies(test_count_if(), in.cbegin(), in.cend(), pred);
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ count_if(exec, iter, iter, non_const(is_even));
+ }
+};
+
+int
+main()
+{
+ test<int32_t>(42, IsEqual<int32_t>(50, OddTag()), [](int32_t j) { return j; });
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_RELEASE_BROKEN)
+ test<int32_t>(42, [](const int32_t&) { return true; }, [](int32_t j) { return j; });
+#endif
+ test<float64_t>(42, IsEqual<float64_t>(50, OddTag()), [](int32_t j) { return float64_t(j); });
+ test<Number>(Number(42, OddTag()), IsEqual<Number>(Number(50, OddTag()), OddTag()),
+ [](int32_t j) { return Number(j, OddTag()); });
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/equal.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/equal.pass.cpp
new file mode 100644
index 00000000000000..a6983eae2ff18d
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/equal.pass.cpp
@@ -0,0 +1,168 @@
+// -*- C++ -*-
+//===-- equal.pass.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+#define CPP14_ENABLED 0
+
+struct UserType
+{
+ size_t key;
+ float32_t f;
+ float64_t d;
+ int32_t i;
+
+ bool
+ operator()(UserType a, UserType b)
+ {
+ return a.key < b.key;
+ }
+ bool
+ operator<(UserType a)
+ {
+ return a.key < key;
+ }
+ bool
+ operator>=(UserType a)
+ {
+ return a.key <= key;
+ }
+ bool
+ operator<=(UserType a)
+ {
+ return a.key >= key;
+ }
+ bool
+ operator==(UserType a)
+ {
+ return a.key == key;
+ }
+ bool
+ operator==(UserType a) const
+ {
+ return a.key == key;
+ }
+ bool
+ operator!=(UserType a)
+ {
+ return a.key != key;
+ }
+ UserType operator!()
+ {
+ UserType tmp;
+ tmp.key = !key;
+ return tmp;
+ }
+ friend std::ostream&
+ operator<<(std::ostream& stream, const UserType a)
+ {
+ stream << a.key;
+ return stream;
+ }
+
+ UserType() : key(-1), f(0.0f), d(0.0), i(0) {}
+ UserType(size_t Number) : key(Number), f(0.0f), d(0.0), i(0) {}
+ UserType&
+ operator=(const UserType& other)
+ {
+ key = other.key;
+ return *this;
+ }
+ UserType(const UserType& other) : key(other.key), f(other.f), d(other.d), i(other.i) {}
+ UserType(UserType&& other) : key(other.key), f(other.f), d(other.d), i(other.i)
+ {
+ other.key = -1;
+ other.f = 0.0f;
+ other.d = 0.0;
+ other.i = 0;
+ }
+};
+
+struct test_one_policy
+{
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, bool is_true_equal)
+ {
+ using namespace std;
+
+ auto expected = equal(first1, last1, first2);
+ auto actual = equal(exec, first1, last1, first2);
+ EXPECT_EQ(expected, actual, "result for equal for random-access iterator, checking against std::equal()");
+
+ // testing bool
+ EXPECT_TRUE(is_true_equal == actual, "result for equal for random-access iterator, bool");
+
+//add C++14 equal symantics tests
+//add more cases for inCopy size less than in
+#if CPP14_ENABLED
+ auto actualr14 = std::equal(in.cbegin(), in.cend(), inCopy.cbegin(), inCopy.cend());
+ EXPECT_EQ(expected, actualr14, "result for equal for random-access iterator");
+#endif
+ }
+};
+
+template <typename T>
+void
+test(size_t bits)
+{
+ for (size_t n = 1; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+
+ // Sequence of odd values
+ Sequence<T> in(n, [bits](size_t k) { return T(2 * HashBits(k, bits - 1) ^ 1); });
+ Sequence<T> inCopy(in);
+
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.end(), inCopy.begin(), true);
+ invoke_on_all_policies(test_one_policy(), in.cbegin(), in.cend(), inCopy.cbegin(), true);
+
+ // testing bool !equal()
+ inCopy[0] = !inCopy[0];
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.end(), inCopy.begin(), false);
+ invoke_on_all_policies(test_one_policy(), in.cbegin(), in.cend(), inCopy.cbegin(), false);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename FirstIterator, typename SecondInterator>
+ void
+ operator()(Policy&& exec, FirstIterator first_iter, SecondInterator second_iter)
+ {
+ equal(exec, first_iter, first_iter, second_iter, second_iter, non_const(std::equal_to<T>()));
+ }
+};
+
+int
+main()
+{
+
+ test<int32_t>(8 * sizeof(int32_t));
+ test<uint16_t>(8 * sizeof(uint16_t));
+ test<float64_t>(53);
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_BOOL_TYPE_RELEASE_64_BROKEN)
+ test<bool>(1);
+#endif
+ test<UserType>(256);
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find.pass.cpp
new file mode 100644
index 00000000000000..54b25c27ea2fb0
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find.pass.cpp
@@ -0,0 +1,96 @@
+// -*- C++ -*-
+//===-- find.pass.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for find
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_find
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator, typename Value>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator first, Iterator last, Value value)
+ {
+ }
+ template <typename Iterator, typename Value>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator first, Iterator last, Value value)
+ {
+ }
+#endif
+
+ template <typename Policy, typename Iterator, typename Value>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Value value)
+ {
+ auto i = std::find(first, last, value);
+ auto j = find(exec, first, last, value);
+ EXPECT_TRUE(i == j, "wrong return value from find");
+ }
+};
+
+template <typename T, typename Value, typename Hit, typename Miss>
+void
+test(Value value, Hit hit, Miss miss)
+{
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, [&](size_t k) -> T { return miss(n ^ k); });
+ // Try different find positions, including not found.
+ // By going backwards, we can add extra matches that are *not* supposed to be found.
+ // The decreasing exponential gives us O(n) total work for the loop since each find takes O(m) time.
+ for (size_t m = n; m > 0; m *= 0.6)
+ {
+ if (m < n)
+ in[m] = hit(n ^ m);
+ invoke_on_all_policies(test_find(), in.begin(), in.end(), value);
+ invoke_on_all_policies(test_find(), in.cbegin(), in.cend(), value);
+ }
+ }
+}
+
+// Type defined for sake of checking that std::find works with asymmetric ==.
+class Weird
+{
+ Number value;
+
+ public:
+ friend bool
+ operator==(Number x, Weird y)
+ {
+ return x == y.value;
+ }
+ Weird(int32_t val, OddTag) : value(val, OddTag()) {}
+};
+
+int
+main()
+{
+ // Note that the "hit" and "miss" functions here avoid overflow issues.
+ test<Number>(Weird(42, OddTag()), [](int32_t) { return Number(42, OddTag()); }, // hit
+ [](int32_t j) { return Number(j == 42 ? 0 : j, OddTag()); }); // miss
+
+ // Test with value that is equal to two different bit patterns (-0.0 and 0.0)
+ test<float32_t>(-0.0, [](int32_t j) { return j & 1 ? 0.0 : -0.0; }, // hit
+ [](int32_t j) { return j == 0 ? ~j : j; }); // miss
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_end.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_end.pass.cpp
new file mode 100644
index 00000000000000..ed0185f7b0c5e0
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_end.pass.cpp
@@ -0,0 +1,123 @@
+// -*- C++ -*-
+//===-- find_end.pass.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator1 b, Iterator1 e, Iterator2 bsub, Iterator2 esub,
+ Predicate pred)
+ {
+ }
+ template <typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 b, Iterator1 e, Iterator2 bsub, Iterator2 esub,
+ Predicate pred)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 b, Iterator1 e, Iterator2 bsub, Iterator2 esub, Predicate pred)
+ {
+ using namespace std;
+ // For find_end
+ {
+ auto expected = find_end(b, e, bsub, esub, pred);
+ auto actual = find_end(exec, b, e, bsub, esub);
+ EXPECT_TRUE(actual == expected, "wrong return result from find_end");
+
+ actual = find_end(exec, b, e, bsub, esub, pred);
+ EXPECT_TRUE(actual == expected, "wrong return result from find_end with a predicate");
+ }
+
+ // For search
+ {
+ auto expected = search(b, e, bsub, esub, pred);
+ auto actual = search(exec, b, e, bsub, esub);
+ EXPECT_TRUE(actual == expected, "wrong return result from search");
+
+ actual = search(exec, b, e, bsub, esub, pred);
+ EXPECT_TRUE(actual == expected, "wrong return result from search with a predicate");
+ }
+ }
+};
+
+template <typename T>
+void
+test(const std::size_t bits)
+{
+
+ const std::size_t max_n1 = 1000;
+ const std::size_t max_n2 = (max_n1 * 10) / 8;
+ Sequence<T> in(max_n1, [bits](std::size_t) { return T(2 * HashBits(max_n1, bits - 1) ^ 1); });
+ Sequence<T> sub(max_n2, [bits](std::size_t) { return T(2 * HashBits(max_n1, bits - 1)); });
+ for (std::size_t n1 = 0; n1 <= max_n1; n1 = n1 <= 16 ? n1 + 1 : size_t(3.1415 * n1))
+ {
+ std::size_t sub_n[] = {0, 1, 3, n1, (n1 * 10) / 8};
+ std::size_t res[] = {0, 1, n1 / 2, n1};
+ for (auto n2 : sub_n)
+ {
+ for (auto r : res)
+ {
+ std::size_t i = r, isub = 0;
+ for (; i < n1 && isub < n2; ++i, ++isub)
+ in[i] = sub[isub];
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.begin() + n1, sub.begin(), sub.begin() + n2,
+ std::equal_to<T>());
+ invoke_on_all_policies(test_one_policy(), in.cbegin(), in.cbegin() + n1, sub.cbegin(),
+ sub.cbegin() + n2, std::equal_to<T>());
+ }
+ }
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename FirstIterator, typename SecondInterator>
+ void
+ operator()(Policy&& exec, FirstIterator first_iter, SecondInterator second_iter)
+ {
+ invoke_if(exec, [&]() {
+ find_end(exec, first_iter, first_iter, second_iter, second_iter, non_const(std::equal_to<T>()));
+ search(exec, first_iter, first_iter, second_iter, second_iter, non_const(std::equal_to<T>()));
+ });
+ }
+};
+
+int
+main()
+{
+ test<int32_t>(8 * sizeof(int32_t));
+ test<uint16_t>(8 * sizeof(uint16_t));
+ test<float64_t>(53);
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_BOOL_TYPE_RELEASE_64_BROKEN)
+ test<bool>(1);
+#endif
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_first_of.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_first_of.pass.cpp
new file mode 100644
index 00000000000000..5b4801e9b3a87b
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_first_of.pass.cpp
@@ -0,0 +1,112 @@
+// -*- C++ -*-
+//===-- find_first_of.pass.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator1 b, Iterator1 e, Iterator2 bsub, Iterator2 esub,
+ Predicate pred)
+ {
+ }
+ template <typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 b, Iterator1 e, Iterator2 bsub, Iterator2 esub,
+ Predicate pred)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 b, Iterator1 e, Iterator2 bsub, Iterator2 esub, Predicate pred)
+ {
+ using namespace std;
+ Iterator1 expected = find_first_of(b, e, bsub, esub, pred);
+ Iterator1 actual = find_first_of(exec, b, e, bsub, esub, pred);
+ EXPECT_TRUE(actual == expected, "wrong return result from find_first_of with a predicate");
+
+ expected = find_first_of(b, e, bsub, esub);
+ actual = find_first_of(exec, b, e, bsub, esub);
+ EXPECT_TRUE(actual == expected, "wrong return result from find_first_of");
+ }
+};
+
+template <typename T, typename Predicate>
+void
+test(Predicate pred)
+{
+
+ const std::size_t max_n1 = 1000;
+ const std::size_t max_n2 = (max_n1 * 10) / 8;
+ Sequence<T> in1(max_n1, [](std::size_t) { return T(1); });
+ Sequence<T> in2(max_n2, [](std::size_t) { return T(0); });
+ for (std::size_t n1 = 0; n1 <= max_n1; n1 = n1 <= 16 ? n1 + 1 : size_t(3.1415 * n1))
+ {
+ std::size_t sub_n[] = {0, 1, n1 / 3, n1, (n1 * 10) / 8};
+ for (const auto n2 : sub_n)
+ {
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.data(), in2.data() + n2, pred);
+
+ in2[n2 / 2] = T(1);
+ invoke_on_all_policies(test_one_policy(), in1.cbegin(), in1.cbegin() + n1, in2.data(), in2.data() + n2,
+ pred);
+
+ if (n2 >= 3)
+ {
+ in2[2 * n2 / 3] = T(1);
+ invoke_on_all_policies(test_one_policy(), in1.cbegin(), in1.cbegin() + n1, in2.begin(),
+ in2.begin() + n2, pred);
+ in2[2 * n2 / 3] = T(0);
+ }
+ in2[n2 / 2] = T(0);
+ }
+ }
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + max_n1 / 10, in1.data(),
+ in1.data() + max_n1 / 10, pred);
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename FirstIterator, typename SecondInterator>
+ void
+ operator()(Policy&& exec, FirstIterator first_iter, SecondInterator second_iter)
+ {
+ invoke_if(exec, [&]() {
+ find_first_of(exec, first_iter, first_iter, second_iter, second_iter, non_const(std::equal_to<T>()));
+ });
+ }
+};
+
+int
+main()
+{
+ test<int32_t>(std::equal_to<int32_t>());
+ test<uint16_t>(std::not_equal_to<uint16_t>());
+ test<float64_t>([](const float64_t x, const float64_t y) { return x * x == y * y; });
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_if.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_if.pass.cpp
new file mode 100644
index 00000000000000..180d003996ddac
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/find_if.pass.cpp
@@ -0,0 +1,109 @@
+// -*- C++ -*-
+//===-- find_if.pass.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for find_if and find_if_not
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_find_if
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator, typename Predicate, typename NotPredicate>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator first, Iterator last, Predicate pred,
+ NotPredicate not_pred)
+ {
+ }
+ template <typename Iterator, typename Predicate, typename NotPredicate>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator first, Iterator last, Predicate pred,
+ NotPredicate not_pred)
+ {
+ }
+#endif
+
+ template <typename Policy, typename Iterator, typename Predicate, typename NotPredicate>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Predicate pred, NotPredicate not_pred)
+ {
+ auto i = std::find_if(first, last, pred);
+ auto j = find_if(exec, first, last, pred);
+ EXPECT_TRUE(i == j, "wrong return value from find_if");
+ auto i_not = find_if_not(exec, first, last, not_pred);
+ EXPECT_TRUE(i_not == i, "wrong return value from find_if_not");
+ }
+};
+
+template <typename T, typename Predicate, typename Hit, typename Miss>
+void
+test(Predicate pred, Hit hit, Miss miss)
+{
+ auto not_pred = [pred](T x) { return !pred(x); };
+ // Try sequences of various lengths.
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, [&](size_t k) -> T { return miss(n ^ k); });
+ // Try different find positions, including not found.
+ // By going backwards, we can add extra matches that are *not* supposed to be found.
+ // The decreasing exponential gives us O(n) total work for the loop since each find takes O(m) time.
+ for (size_t m = n; m > 0; m *= 0.6)
+ {
+ if (m < n)
+ in[m] = hit(n ^ m);
+ invoke_on_all_policies(test_find_if(), in.begin(), in.end(), pred, not_pred);
+ invoke_on_all_policies(test_find_if(), in.cbegin(), in.cend(), pred, not_pred);
+ }
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+
+ invoke_if(exec, [&]() {
+ find_if(exec, iter, iter, non_const(is_even));
+ find_if_not(exec, iter, iter, non_const(is_even));
+ });
+ }
+};
+
+int
+main()
+{
+#if !defined(_PSTL_ICC_17_TEST_MAC_RELEASE_32_BROKEN)
+ // Note that the "hit" and "miss" functions here avoid overflow issues.
+ test<Number>(IsMultiple(5, OddTag()), [](int32_t j) { return Number(j - j % 5, OddTag()); }, // hit
+ [](int32_t j) { return Number(j % 5 == 0 ? j ^ 1 : j, OddTag()); }); // miss
+#endif
+
+ // Try type for which algorithm can really be vectorized.
+ test<float32_t>([](float32_t x) { return x >= 0; }, [](float32_t j) { return j * j; },
+ [](float32_t j) { return -1 - j * j; });
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/for_each.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/for_each.pass.cpp
new file mode 100644
index 00000000000000..14a0ad4f32b124
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/for_each.pass.cpp
@@ -0,0 +1,102 @@
+// -*- C++ -*-
+//===-- for_each.pass.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename Type>
+struct Gen
+{
+ Type
+ operator()(std::size_t k)
+ {
+ return Type(k % 5 != 1 ? 3 * k - 7 : 0);
+ };
+};
+
+template <typename T>
+struct Flip
+{
+ int32_t val;
+ Flip(int32_t y) : val(y) {}
+ T
+ operator()(T& x) const
+ {
+ return x = val - x;
+ }
+};
+
+struct test_one_policy
+{
+ template <typename Policy, typename Iterator, typename Size>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Iterator expected_first, Iterator expected_last, Size n)
+ {
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+
+ // Try for_each
+ std::for_each(expected_first, expected_last, Flip<T>(1));
+ for_each(exec, first, last, Flip<T>(1));
+ EXPECT_EQ_N(expected_first, first, n, "wrong effect from for_each");
+
+ // Try for_each_n
+ std::for_each_n(std::execution::seq, expected_first, n, Flip<T>(1));
+ for_each_n(exec, first, n, Flip<T>(1));
+ EXPECT_EQ_N(expected_first, first, n, "wrong effect from for_each_n");
+ }
+};
+
+template <typename T>
+void
+test()
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> inout(n, Gen<T>());
+ Sequence<T> expected(n, Gen<T>());
+ invoke_on_all_policies(test_one_policy(), inout.begin(), inout.end(), expected.begin(), expected.end(),
+ inout.size());
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ invoke_if(exec, [&]() {
+ auto f = [](typename std::iterator_traits<Iterator>::reference x) { x = x + 1; };
+
+ for_each(exec, iter, iter, non_const(f));
+ for_each_n(exec, iter, 0, non_const(f));
+ });
+ }
+};
+
+int
+main()
+{
+ test<int32_t>();
+ test<uint16_t>();
+ test<float64_t>();
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/mismatch.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/mismatch.pass.cpp
new file mode 100644
index 00000000000000..4d83ad6de7824c
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/mismatch.pass.cpp
@@ -0,0 +1,132 @@
+// -*- C++ -*-
+//===-- mismatch.pass.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_mismatch
+{
+ template <typename Policy, typename Iterator1, typename Iterator2>
+ void
+ operator()(Policy&& exec, Iterator1 first1, Iterator1 last1, Iterator2 first2)
+ {
+ using namespace std;
+ typedef typename iterator_traits<Iterator1>::value_type T;
+ {
+ const auto expected = std::mismatch(first1, last1, first2, std::equal_to<T>());
+ const auto res3 = mismatch(exec, first1, last1, first2, std::equal_to<T>());
+ EXPECT_TRUE(expected == res3, "wrong return result from mismatch");
+ const auto res4 = mismatch(exec, first1, last1, first2);
+ EXPECT_TRUE(expected == res4, "wrong return result from mismatch");
+ }
+ }
+ template <typename Policy, typename Iterator1, typename Iterator2>
+ void
+ operator()(Policy&& exec, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
+ {
+ using namespace std;
+ typedef typename iterator_traits<Iterator1>::value_type T;
+ {
+ const auto expected = mismatch(std::execution::seq, first1, last1, first2, last2, std::equal_to<T>());
+ const auto res1 = mismatch(exec, first1, last1, first2, last2, std::equal_to<T>());
+ EXPECT_TRUE(expected == res1, "wrong return result from mismatch");
+ const auto res2 = mismatch(exec, first1, last1, first2, last2);
+ EXPECT_TRUE(expected == res2, "wrong return result from mismatch");
+ }
+ }
+};
+
+template <typename T>
+void
+test_mismatch_by_type()
+{
+ using namespace std;
+ for (size_t size = 0; size <= 100000; size = size <= 16 ? size + 1 : size_t(3.1415 * size))
+ {
+ const T val = T(-1);
+ Sequence<T> in(size, [](size_t v) -> T { return T(v % 100); });
+ {
+ Sequence<T> in2(in);
+ invoke_on_all_policies(test_mismatch(), in.begin(), in.end(), in2.begin(), in2.end());
+ invoke_on_all_policies(test_mismatch(), in.begin(), in.end(), in2.begin());
+
+ const size_t min_size = 3;
+ if (size > min_size)
+ {
+ const size_t idx_for_1 = size / min_size;
+ in[idx_for_1] = val, in[idx_for_1 + 1] = val, in[idx_for_1 + 2] = val;
+ invoke_on_all_policies(test_mismatch(), in.begin(), in.end(), in2.begin(), in2.end());
+ invoke_on_all_policies(test_mismatch(), in.begin(), in.end(), in2.begin());
+ }
+
+ const size_t idx_for_2 = 500;
+ if (size >= idx_for_2 - 1)
+ {
+ in2[size / idx_for_2] = val;
+ invoke_on_all_policies(test_mismatch(), in.cbegin(), in.cend(), in2.cbegin(), in2.cend());
+ invoke_on_all_policies(test_mismatch(), in.cbegin(), in.cend(), in2.cbegin());
+ }
+ }
+ {
+ Sequence<T> in2(100, [](size_t v) -> T { return T(v); });
+ invoke_on_all_policies(test_mismatch(), in2.begin(), in2.end(), in.begin(), in.end());
+ // We can't call std::mismatch with semantic below when size of second sequence less than size of first sequence
+ if (in2.size() <= in.size())
+ invoke_on_all_policies(test_mismatch(), in2.begin(), in2.end(), in.begin());
+
+ const size_t idx = 97;
+ in2[idx] = val;
+ in2[idx + 1] = val;
+ invoke_on_all_policies(test_mismatch(), in.cbegin(), in.cend(), in2.cbegin(), in2.cend());
+ if (in.size() <= in2.size())
+ invoke_on_all_policies(test_mismatch(), in.cbegin(), in.cend(), in2.cbegin());
+ }
+ {
+ Sequence<T> in2({});
+ invoke_on_all_policies(test_mismatch(), in2.begin(), in2.end(), in.begin(), in.end());
+
+ invoke_on_all_policies(test_mismatch(), in.cbegin(), in.cend(), in2.cbegin(), in2.cend());
+ if (in.size() == 0)
+ invoke_on_all_policies(test_mismatch(), in.cbegin(), in.cend(), in2.cbegin());
+ }
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename FirstIterator, typename SecondInterator>
+ void
+ operator()(Policy&& exec, FirstIterator first_iter, SecondInterator second_iter)
+ {
+ mismatch(exec, first_iter, first_iter, second_iter, second_iter, non_const(std::less<T>()));
+ }
+};
+
+int
+main()
+{
+
+ test_mismatch_by_type<int32_t>();
+ test_mismatch_by_type<float64_t>();
+ test_mismatch_by_type<Wrapper<int32_t>>();
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/none_of.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/none_of.pass.cpp
new file mode 100644
index 00000000000000..dbdcd54438c439
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/none_of.pass.cpp
@@ -0,0 +1,101 @@
+// -*- C++ -*-
+//===-- none_of.pass.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+/*
+ TODO: consider implementing the following tests for a better code coverage
+ - correctness
+ - bad input argument (if applicable)
+ - data corruption around/of input and output
+ - correctly work with nested parallelism
+ - check that algorithm does not require anything more than is described in its requirements section
+*/
+
+using namespace TestUtils;
+
+struct test_none_of
+{
+ template <typename ExecutionPolicy, typename Iterator, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator begin, Iterator end, Predicate pred, bool expected)
+ {
+
+ auto actualr = std::none_of(exec, begin, end, pred);
+ EXPECT_EQ(expected, actualr, "result for none_of");
+ }
+};
+
+template <typename T>
+void
+test(size_t bits)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+
+ // Sequence of odd values
+ Sequence<T> in(n, [n, bits](size_t) { return T(2 * HashBits(n, bits - 1) ^ 1); });
+
+ // Even value, or false when T is bool.
+ T spike(2 * HashBits(n, bits - 1));
+
+ invoke_on_all_policies(test_none_of(), in.begin(), in.end(), is_equal_to<T>(spike), true);
+ invoke_on_all_policies(test_none_of(), in.cbegin(), in.cend(), is_equal_to<T>(spike), true);
+ if (n > 0)
+ {
+ // Sprinkle in a hit
+ in[2 * n / 3] = spike;
+ invoke_on_all_policies(test_none_of(), in.begin(), in.end(), is_equal_to<T>(spike), false);
+ invoke_on_all_policies(test_none_of(), in.cbegin(), in.cend(), is_equal_to<T>(spike), false);
+
+ // Sprinkle in a few more hits
+ in[n / 3] = spike;
+ in[n / 2] = spike;
+ invoke_on_all_policies(test_none_of(), in.begin(), in.end(), is_equal_to<T>(spike), false);
+ invoke_on_all_policies(test_none_of(), in.cbegin(), in.cend(), is_equal_to<T>(spike), false);
+ }
+ }
+}
+
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ auto is_even = [&](float64_t v) {
+ uint32_t i = (uint32_t)v;
+ return i % 2 == 0;
+ };
+ none_of(exec, iter, iter, non_const(is_even));
+ }
+};
+
+int
+main()
+{
+ test<int32_t>(8 * sizeof(int32_t));
+ test<uint16_t>(8 * sizeof(uint16_t));
+ test<float64_t>(53);
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_BOOL_TYPE_RELEASE_64_BROKEN)
+ test<bool>(1);
+#endif
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/nth_element.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/nth_element.pass.cpp
new file mode 100644
index 00000000000000..f3e43da39a3a91
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/nth_element.pass.cpp
@@ -0,0 +1,175 @@
+// -*- C++ -*-
+//===-- nth_element.pass.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iostream>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// User defined type with minimal requirements
+template <typename T>
+struct DataType
+{
+ explicit DataType(int32_t k) : my_val(k) {}
+ DataType(DataType&& input)
+ {
+ my_val = std::move(input.my_val);
+ input.my_val = T(0);
+ }
+ DataType&
+ operator=(DataType&& input)
+ {
+ my_val = std::move(input.my_val);
+ input.my_val = T(0);
+ return *this;
+ }
+ T
+ get_val() const
+ {
+ return my_val;
+ }
+
+ friend std::ostream&
+ operator<<(std::ostream& stream, const DataType<T>& input)
+ {
+ return stream << input.my_val;
+ }
+
+ private:
+ T my_val;
+};
+
+template <typename T>
+bool
+is_equal(const DataType<T>& x, const DataType<T>& y)
+{
+ return x.get_val() == y.get_val();
+}
+
+template <typename T>
+bool
+is_equal(const T& x, const T& y)
+{
+ return x == y;
+}
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1, typename Size, typename Generator1, typename Generator2, typename Compare>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::unsequenced_policy, Iterator1 first1, Iterator1 last1, Iterator1 first2,
+ Iterator1 last2, Size n, Size m, Generator1 generator1, Generator2 generator2, Compare comp)
+ {
+ }
+ template <typename Iterator1, typename Size, typename Generator1, typename Generator2, typename Compare>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 first1, Iterator1 last1, Iterator1 first2,
+ Iterator1 last2, Size n, Size m, Generator1 generator1, Generator2 generator2, Compare comp)
+ {
+ }
+#endif
+
+ // nth_element works only with random access iterators
+ template <typename Policy, typename Iterator1, typename Size, typename Generator1, typename Generator2,
+ typename Compare>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(Policy&& exec, Iterator1 first1, Iterator1 last1, Iterator1 first2, Iterator1 last2, Size n, Size m,
+ Generator1 generator1, Generator2 generator2, Compare comp)
+ {
+
+ using T = typename std::iterator_traits<Iterator1>::value_type;
+ const Iterator1 mid1 = std::next(first1, m);
+ const Iterator1 mid2 = std::next(first2, m);
+
+ fill_data(first1, mid1, generator1);
+ fill_data(mid1, last1, generator2);
+ fill_data(first2, mid2, generator1);
+ fill_data(mid2, last2, generator2);
+ std::nth_element(first1, mid1, last1, comp);
+ std::nth_element(exec, first2, mid2, last2, comp);
+ if (m > 0 && m < n)
+ {
+ EXPECT_TRUE(is_equal(*mid1, *mid2), "wrong result from nth_element with predicate");
+ }
+ EXPECT_TRUE(std::find_first_of(first2, mid2, mid2, last2, [comp](T& x, T& y) { return comp(y, x); }) == mid2,
+ "wrong effect from nth_element with predicate");
+ }
+
+ template <typename Policy, typename Iterator1, typename Size, typename Generator1, typename Generator2,
+ typename Compare>
+ typename std::enable_if<!is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(Policy&&, Iterator1, Iterator1, Iterator1, Iterator1, Size, Size, Generator1, Generator2, Compare)
+ {
+ }
+};
+
+template <typename T, typename Generator1, typename Generator2, typename Compare>
+void
+test_by_type(Generator1 generator1, Generator2 generator2, Compare comp)
+{
+ using namespace std;
+ size_t max_size = 10000;
+ Sequence<T> in1(max_size, [](size_t v) { return T(v); });
+ Sequence<T> exp(max_size, [](size_t v) { return T(v); });
+ size_t m;
+
+ for (size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ m = 0;
+ invoke_on_all_policies(test_one_policy(), exp.begin(), exp.begin() + n, in1.begin(), in1.begin() + n, n, m,
+ generator1, generator2, comp);
+ m = n / 7;
+ invoke_on_all_policies(test_one_policy(), exp.begin(), exp.begin() + n, in1.begin(), in1.begin() + n, n, m,
+ generator1, generator2, comp);
+ m = 3 * n / 5;
+ invoke_on_all_policies(test_one_policy(), exp.begin(), exp.begin() + n, in1.begin(), in1.begin() + n, n, m,
+ generator1, generator2, comp);
+ }
+ invoke_on_all_policies(test_one_policy(), exp.begin(), exp.begin() + max_size, in1.begin(), in1.begin() + max_size,
+ max_size, max_size, generator1, generator2, comp);
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ invoke_if(exec, [&]() { nth_element(exec, iter, iter, iter, non_const(std::less<T>())); });
+ }
+};
+
+int
+main()
+{
+ test_by_type<int32_t>([](int32_t i) { return 10 * i; }, [](int32_t i) { return i + 1; }, std::less<int32_t>());
+ test_by_type<int32_t>([](int32_t) { return 0; }, [](int32_t) { return 0; }, std::less<int32_t>());
+
+ test_by_type<float64_t>([](int32_t i) { return -2 * i; }, [](int32_t i) { return -(2 * i + 1); },
+ [](const float64_t x, const float64_t y) { return x > y; });
+
+ test_by_type<DataType<float32_t>>(
+ [](int32_t i) { return DataType<float32_t>(2 * i + 1); }, [](int32_t i) { return DataType<float32_t>(2 * i); },
+ [](const DataType<float32_t>& x, const DataType<float32_t>& y) { return x.get_val() < y.get_val(); });
+
+ test_algo_basic_single<int32_t>(run_for_rnd<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.nonmodifying/search_n.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/search_n.pass.cpp
new file mode 100644
index 00000000000000..573f364edef21b
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.nonmodifying/search_n.pass.cpp
@@ -0,0 +1,109 @@
+// -*- C++ -*-
+//===-- search_n.pass.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator, typename Size, typename T, typename Predicate>
+ void
+ operator()(pstl::execution::unsequenced_policy, Iterator b, Iterator e, Size count, const T& value, Predicate pred)
+ {
+ }
+ template <typename Iterator, typename Size, typename T, typename Predicate>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator b, Iterator e, Size count, const T& value,
+ Predicate pred)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator, typename Size, typename T, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator b, Iterator e, Size count, const T& value, Predicate pred)
+ {
+ using namespace std;
+ auto expected = search_n(b, e, count, value, pred);
+ auto actual = search_n(exec, b, e, count, value);
+ EXPECT_TRUE(actual == expected, "wrong return result from search_n");
+
+ actual = search_n(exec, b, e, count, value, pred);
+ EXPECT_TRUE(actual == expected, "wrong return result from search_n with a predicate");
+ }
+};
+
+template <typename T>
+void
+test()
+{
+
+ const std::size_t max_n1 = 100000;
+ const T value = T(1);
+ for (std::size_t n1 = 0; n1 <= max_n1; n1 = n1 <= 16 ? n1 + 1 : size_t(3.1415 * n1))
+ {
+ std::size_t sub_n[] = {0, 1, 3, n1, (n1 * 10) / 8};
+ std::size_t res[] = {0, 1, n1 / 2, n1};
+ for (auto n2 : sub_n)
+ {
+ // Some of standard libraries return "first" in this case. We return "last" according to the standard
+ if (n2 == 0)
+ {
+ continue;
+ }
+ for (auto r : res)
+ {
+ Sequence<T> in(n1, [](std::size_t) { return T(0); });
+ std::size_t i = r, isub = 0;
+ for (; i < n1 && isub < n2; ++i, ++isub)
+ in[i] = value;
+
+ invoke_on_all_policies(test_one_policy(), in.begin(), in.begin() + n1, n2, value, std::equal_to<T>());
+ invoke_on_all_policies(test_one_policy(), in.cbegin(), in.cbegin() + n1, n2, value, std::equal_to<T>());
+ }
+ }
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ invoke_if(exec, [&]() { search_n(exec, iter, iter, 0, T(0), non_const(std::equal_to<T>())); });
+ }
+};
+
+int
+main()
+{
+ test<int32_t>();
+ test<uint16_t>();
+ test<float64_t>();
+#if !defined(_PSTL_ICC_16_17_TEST_REDUCTION_BOOL_TYPE_RELEASE_64_BROKEN)
+ test<bool>();
+#endif
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/alg.heap.operations/is_heap.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.heap.operations/is_heap.pass.cpp
new file mode 100644
index 00000000000000..08eca8eed1c527
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.heap.operations/is_heap.pass.cpp
@@ -0,0 +1,146 @@
+// -*- C++ -*-
+//===-- is_heap.pass.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for is_heap, is_heap_until
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+#include <iostream>
+
+using namespace TestUtils;
+
+struct WithCmpOp
+{
+ int32_t _first;
+ int32_t _second;
+ WithCmpOp() : _first(0), _second(0){};
+ explicit WithCmpOp(int32_t x) : _first(x), _second(x){};
+ bool
+ operator<(const WithCmpOp& rhs) const
+ {
+ return this->_first < rhs._first;
+ }
+};
+
+struct test_is_heap
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator, typename Predicate>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::unsequenced_policy, Iterator first, Iterator last, Predicate pred)
+ {
+ }
+ template <typename Iterator, typename Predicate>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator first, Iterator last, Predicate pred)
+ {
+ }
+#endif
+
+ template <typename Policy, typename Iterator, typename Predicate>
+ typename std::enable_if<is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
+ operator()(Policy&& exec, Iterator first, Iterator last, Predicate pred)
+ {
+ using namespace std;
+ // is_heap
+ {
+ bool expected = is_heap(first, last);
+ bool actual = is_heap(exec, first, last);
+ EXPECT_TRUE(expected == actual, "wrong return value from is_heap");
+ }
+ // is_heap with predicate
+ {
+ bool expected = is_heap(first, last, pred);
+ bool actual = is_heap(exec, first, last, pred);
+ EXPECT_TRUE(expected == actual, "wrong return value from is_heap with predicate");
+ }
+ // is_heap_until
+ {
+ Iterator expected = is_heap_until(first, last);
+ Iterator actual = is_heap_until(exec, first, last);
+ EXPECT_TRUE(expected == actual, "wrong return value from is_heap_until");
+ }
+ // is_heap_until with predicate
+ {
+ const Iterator expected = is_heap_until(first, last, pred);
+ const auto y = std::distance(first, expected);
+ const Iterator actual = is_heap_until(exec, first, last, pred);
+ const auto x = std::distance(first, actual);
+ EXPECT_TRUE(expected == actual, "wrong return value from is_heap_until with predicate");
+ EXPECT_EQ(x, y, "both iterators should be the same distance away from 'first'");
+ }
+ }
+
+ // is_heap, is_heap_until works only with random access iterators
+ template <typename Policy, typename Iterator, typename Predicate>
+ typename std::enable_if<!is_same_iterator_category<Iterator, std::random_access_iterator_tag>::value, void>::type
+ operator()(Policy&&, Iterator, Iterator, Predicate)
+ {
+ }
+};
+
+template <typename T, typename Comp>
+void
+test_is_heap_by_type(Comp comp)
+{
+ using namespace std;
+
+ const size_t max_size = 100000;
+ for (size_t n = 0; n <= max_size; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, [](size_t v) -> T { return T(v); });
+
+ invoke_on_all_policies(test_is_heap(), in.begin(), in.end(), comp);
+
+ std::make_heap(in.begin(), in.begin() + n / 4, comp);
+ invoke_on_all_policies(test_is_heap(), in.cbegin(), in.cend(), comp);
+
+ std::make_heap(in.begin(), in.begin() + n / 3, comp);
+ invoke_on_all_policies(test_is_heap(), in.begin(), in.end(), comp);
+
+ std::make_heap(in.begin(), in.end(), comp);
+ invoke_on_all_policies(test_is_heap(), in.cbegin(), in.cend(), comp);
+ }
+
+ Sequence<T> in(max_size / 10, [](size_t) -> T { return T(1); });
+ invoke_on_all_policies(test_is_heap(), in.begin(), in.end(), comp);
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ invoke_if(exec, [&]() {
+ is_heap(exec, iter, iter, non_const(std::less<T>()));
+ is_heap_until(exec, iter, iter, non_const(std::less<T>()));
+ });
+ }
+};
+
+int
+main()
+{
+ test_is_heap_by_type<float32_t>(std::greater<float32_t>());
+ test_is_heap_by_type<WithCmpOp>(std::less<WithCmpOp>());
+ test_is_heap_by_type<uint64_t>([](uint64_t x, uint64_t y) { return x % 100 < y % 100; });
+
+ test_algo_basic_single<int32_t>(run_for_rnd<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
new file mode 100644
index 00000000000000..cb920574ba353f
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
@@ -0,0 +1,175 @@
+// -*- C++ -*-
+//===-- lexicographical_compare.pass.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iostream>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_one_policy
+{
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename Predicate>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 begin1, Iterator1 end1, Iterator2 begin2, Iterator2 end2,
+ Predicate pred)
+ {
+ const bool expected = std::lexicographical_compare(begin1, end1, begin2, end2, pred);
+ const bool actual = std::lexicographical_compare(exec, begin1, end1, begin2, end2, pred);
+ EXPECT_TRUE(actual == expected, "wrong return result from lexicographical compare with predicate");
+ }
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 begin1, Iterator1 end1, Iterator2 begin2, Iterator2 end2)
+ {
+ const bool expected = std::lexicographical_compare(begin1, end1, begin2, end2);
+ const bool actual = std::lexicographical_compare(exec, begin1, end1, begin2, end2);
+ EXPECT_TRUE(actual == expected, "wrong return result from lexicographical compare without predicate");
+ }
+};
+
+template <typename T1, typename T2, typename Predicate>
+void
+test(Predicate pred)
+{
+
+ const std::size_t max_n = 1000000;
+ Sequence<T1> in1(max_n, [](std::size_t k) { return T1(k); });
+ Sequence<T2> in2(2 * max_n, [](std::size_t k) { return T2(k); });
+
+ std::size_t n2;
+
+ // Test case: Call algorithm's version without predicate.
+ invoke_on_all_policies(test_one_policy(), in1.cbegin(), in1.cbegin() + max_n, in2.cbegin() + 3 * max_n / 10,
+ in2.cbegin() + 5 * max_n / 10);
+
+ // Test case: If one range is a prefix of another, the shorter range is lexicographically less than the other.
+ std::size_t max_n2 = max_n / 10;
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + max_n, in2.cbegin(), in2.cbegin() + max_n2,
+ pred);
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + max_n, in2.begin() + max_n2,
+ in2.begin() + 3 * max_n2, pred);
+
+ // Test case: If one range is a prefix of another, the shorter range is lexicographically less than the other.
+ max_n2 = 2 * max_n;
+ invoke_on_all_policies(test_one_policy(), in1.cbegin(), in1.cbegin() + max_n, in2.begin(), in2.begin() + max_n2,
+ pred);
+
+ for (std::size_t n1 = 0; n1 <= max_n; n1 = n1 <= 16 ? n1 + 1 : std::size_t(3.1415 * n1))
+ {
+ // Test case: If two ranges have equivalent elements and are of the same length, then the ranges are lexicographically equal.
+ n2 = n1;
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.begin(), in2.begin() + n2, pred);
+
+ n2 = n1;
+ // Test case: two ranges have different elements and are of the same length (second sequence less than first)
+ std::size_t ind = n1 / 2;
+ in2[ind] = T2(-1);
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.begin(), in2.begin() + n2, pred);
+ in2[ind] = T2(ind);
+
+ // Test case: two ranges have different elements and are of the same length (first sequence less than second)
+ ind = n1 / 5;
+ in1[ind] = T1(-1);
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.cbegin(), in2.cbegin() + n2, pred);
+ in1[ind] = T1(ind);
+ }
+}
+
+template <typename Predicate>
+void
+test_string(Predicate pred)
+{
+
+ const std::size_t max_n = 1000000;
+ std::string in1 = "";
+ std::string in2 = "";
+ for (std::size_t n1 = 0; n1 <= max_n; ++n1)
+ {
+ in1 += n1;
+ }
+
+ for (std::size_t n1 = 0; n1 <= 2 * max_n; ++n1)
+ {
+ in2 += n1;
+ }
+
+ std::size_t n2;
+
+ for (std::size_t n1 = 0; n1 < in1.size(); n1 = n1 <= 16 ? n1 + 1 : std::size_t(3.1415 * n1))
+ {
+ // Test case: If two ranges have equivalent elements and are of the same length, then the ranges are lexicographically equal.
+ n2 = n1;
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.begin(), in2.begin() + n2, pred);
+
+ n2 = n1;
+ // Test case: two ranges have different elements and are of the same length (second sequence less than first)
+ in2[n1 / 2] = 'a';
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.begin(), in2.begin() + n2, pred);
+
+ // Test case: two ranges have different elements and are of the same length (first sequence less than second)
+ in1[n1 / 5] = 'a';
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.begin() + n1, in2.cbegin(), in2.cbegin() + n2, pred);
+ }
+ invoke_on_all_policies(test_one_policy(), in1.cbegin(), in1.cbegin() + max_n, in2.cbegin() + 3 * max_n / 10,
+ in2.cbegin() + 5 * max_n / 10);
+}
+
+template <typename T>
+struct LocalWrapper
+{
+ explicit LocalWrapper(std::size_t k) : my_val(k) {}
+ bool
+ operator<(const LocalWrapper<T>& w) const
+ {
+ return my_val < w.my_val;
+ }
+
+ private:
+ T my_val;
+};
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename FirstIterator, typename SecondInterator>
+ void
+ operator()(Policy&& exec, FirstIterator first_iter, SecondInterator second_iter)
+ {
+ invoke_if(exec, [&]() {
+ lexicographical_compare(exec, first_iter, first_iter, second_iter, second_iter, non_const(std::less<T>()));
+ });
+ }
+};
+
+int
+main()
+{
+ test<uint16_t, float64_t>(std::less<float64_t>());
+ test<float32_t, int32_t>(std::greater<float32_t>());
+#if !defined(_PSTL_ICC_18_TEST_EARLY_EXIT_AVX_RELEASE_BROKEN)
+ test<float64_t, int32_t>([](const float64_t x, const int32_t y) { return x * x < y * y; });
+#endif
+ test<LocalWrapper<int32_t>, LocalWrapper<int32_t>>(
+ [](const LocalWrapper<int32_t>& x, const LocalWrapper<int32_t>& y) { return x < y; });
+ test_string([](const char x, const char y) { return x < y; });
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/alg.min.max/minmax_element.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.min.max/minmax_element.pass.cpp
new file mode 100644
index 00000000000000..715b25014992da
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.min.max/minmax_element.pass.cpp
@@ -0,0 +1,192 @@
+// -*- C++ -*-
+//===-- minmax_element.pass.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+#include <set>
+#include <cmath>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct check_minelement
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator begin, Iterator end)
+ {
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+ const Iterator expect = std::min_element(begin, end);
+ const Iterator result = std::min_element(exec, begin, end);
+ const Iterator result_pred = std::min_element(exec, begin, end, std::less<T>());
+ EXPECT_TRUE(expect == result, "wrong return result from min_element");
+ EXPECT_TRUE(expect == result_pred, "wrong return result from min_element");
+ }
+};
+
+struct check_maxelement
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator begin, Iterator end)
+ {
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+ const Iterator expect = std::max_element(begin, end);
+ const Iterator result = std::max_element(exec, begin, end);
+ const Iterator result_pred = std::max_element(exec, begin, end, std::less<T>());
+ EXPECT_TRUE(expect == result, "wrong return result from max_element");
+ EXPECT_TRUE(expect == result_pred, "wrong return result from max_element");
+ }
+};
+
+struct check_minmaxelement
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator begin, Iterator end)
+ {
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+ const std::pair<Iterator, Iterator> expect = std::minmax_element(begin, end);
+ const std::pair<Iterator, Iterator> got = std::minmax_element(exec, begin, end);
+ const std::pair<Iterator, Iterator> got_pred = std::minmax_element(exec, begin, end, std::less<T>());
+ EXPECT_TRUE(expect.first == got.first, "wrong return result from minmax_element (min part)");
+ EXPECT_TRUE(expect.second == got.second, "wrong return result from minmax_element (max part)");
+ EXPECT_TRUE(expect == got_pred, "wrong return result from minmax_element");
+ }
+};
+
+template <typename T>
+struct sequence_wrapper
+{
+ TestUtils::Sequence<T> seq;
+ const T min_value;
+ const T max_value;
+ static const std::size_t bits = 30; // We assume that T can handle signed 2^bits+1 value
+
+ // TestUtils::HashBits returns value between 0 and (1<<bits)-1,
+ // therefore we could threat 1<<bits as maximum and -(1<<bits) as a minimum
+ sequence_wrapper(std::size_t n) : seq(n), min_value(-(1 << bits)), max_value(1 << bits) {}
+
+ void
+ pattern_fill()
+ {
+ seq.fill([](std::size_t i) -> T { return T(TestUtils::HashBits(i, bits)); });
+ }
+
+ // sets first one at position `at` and bunch of them farther
+ void
+ set_desired_value(std::size_t at, T value)
+ {
+ if (seq.size() == 0)
+ return;
+ seq[at] = value;
+
+ //Producing serveral red herrings
+ for (std::size_t i = at + 1; i < seq.size(); i += 1 + TestUtils::HashBits(i, 5))
+ seq[i] = value;
+ }
+};
+
+template <typename T>
+void
+test_by_type(std::size_t n)
+{
+ sequence_wrapper<T> wseq(n);
+
+ // to avoid overtesing we use std::set to leave only unique indexes
+ std::set<std::size_t> targets{0};
+ if (n > 1)
+ {
+ targets.insert(1);
+ targets.insert(2.718282 * n / 3);
+ targets.insert(n / 2);
+ targets.insert(n / 7.389056);
+ targets.insert(n - 1); // last
+ }
+
+ for (std::set<std::size_t>::iterator it = targets.begin(); it != targets.end(); ++it)
+ {
+ wseq.pattern_fill();
+ wseq.set_desired_value(*it, wseq.min_value);
+ TestUtils::invoke_on_all_policies(check_minelement(), wseq.seq.cbegin(), wseq.seq.cend());
+ TestUtils::invoke_on_all_policies(check_minelement(), wseq.seq.begin(), wseq.seq.end());
+
+ wseq.set_desired_value(*it, wseq.max_value);
+ TestUtils::invoke_on_all_policies(check_maxelement(), wseq.seq.cbegin(), wseq.seq.cend());
+ TestUtils::invoke_on_all_policies(check_maxelement(), wseq.seq.begin(), wseq.seq.end());
+
+ if (targets.size() > 1)
+ {
+ for (std::set<std::size_t>::reverse_iterator rit = targets.rbegin(); rit != targets.rend(); ++rit)
+ {
+ if (*rit == *it) // we requires at least 2 unique indexes in targets
+ break;
+ wseq.pattern_fill();
+ wseq.set_desired_value(*it, wseq.min_value); // setting minimum element
+ wseq.set_desired_value(*rit, wseq.max_value); // setting maximum element
+ TestUtils::invoke_on_all_policies(check_minmaxelement(), wseq.seq.cbegin(), wseq.seq.cend());
+ TestUtils::invoke_on_all_policies(check_minmaxelement(), wseq.seq.begin(), wseq.seq.end());
+ }
+ }
+ else
+ { // we must check this corner case; it can not be tested in loop above
+ TestUtils::invoke_on_all_policies(check_minmaxelement(), wseq.seq.cbegin(), wseq.seq.cend());
+ TestUtils::invoke_on_all_policies(check_minmaxelement(), wseq.seq.begin(), wseq.seq.end());
+ }
+ }
+}
+
+// should provide minimal requirements only
+struct OnlyLessCompare
+{
+ int32_t val;
+ OnlyLessCompare() : val(0) {}
+ OnlyLessCompare(int32_t val_) : val(val_) {}
+ bool
+ operator<(const OnlyLessCompare& other) const
+ {
+ return val < other.val;
+ }
+};
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ max_element(exec, iter, iter, non_const(std::less<T>()));
+ min_element(exec, iter, iter, non_const(std::less<T>()));
+ minmax_element(exec, iter, iter, non_const(std::less<T>()));
+ }
+};
+
+int
+main()
+{
+ using TestUtils::float64_t;
+ const std::size_t N = 100000;
+
+ for (std::size_t n = 0; n < N; n = n < 16 ? n + 1 : size_t(3.14159 * n))
+ {
+ test_by_type<float64_t>(n);
+ test_by_type<OnlyLessCompare>(n);
+ }
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << TestUtils::done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/includes.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/includes.pass.cpp
new file mode 100644
index 00000000000000..778d265f9ca7f9
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/includes.pass.cpp
@@ -0,0 +1,106 @@
+// -*- C++ -*-
+//===-- includes.pass.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <algorithm>
+#include <cmath>
+#include <execution>
+#include <functional>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct Num
+{
+ T val;
+ explicit Num(const T& v) : val(v) {}
+
+ //for "includes" checks
+ template <typename T1>
+ bool
+ operator<(const Num<T1>& v1) const
+ {
+ return val < v1.val;
+ }
+
+ //The types Type1 and Type2 must be such that an object of type InputIt can be dereferenced and then implicitly converted to both of them
+ template <typename T1>
+ operator Num<T1>() const
+ {
+ return Num<T1>((T1)val);
+ }
+};
+
+struct test_one_policy
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<!TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
+ Compare comp)
+ {
+
+ auto expect_res = std::includes(first1, last1, first2, last2, comp);
+ auto res = std::includes(exec, first1, last1, first2, last2, comp);
+
+ EXPECT_TRUE(expect_res == res, "wrong result for includes");
+ }
+
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&&, InputIterator1, InputIterator1, InputIterator2, InputIterator2, Compare)
+ {
+ }
+};
+
+template <typename T1, typename T2, typename Compare>
+void
+test_includes(Compare compare)
+{
+
+ const std::size_t n_max = 1000000;
+
+ // The rand()%(2*n+1) encourages generation of some duplicates.
+ std::srand(42);
+
+ for (std::size_t n = 0; n < n_max; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ for (std::size_t m = 0; m < n_max; m = m <= 16 ? m + 1 : size_t(2.71828 * m))
+ {
+ //prepare the input ranges
+ Sequence<T1> in1(n, [](std::size_t k) { return rand() % (2 * k + 1); });
+ Sequence<T2> in2(m, [](std::size_t k) { return rand() % (k + 1); });
+
+ std::sort(in1.begin(), in1.end(), compare);
+ std::sort(in2.begin(), in2.end(), compare);
+
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.end(), in2.cbegin(), in2.cend(), compare);
+
+ //test w/ non constant predicate
+ if (n < 5 && m < 5)
+ invoke_on_all_policies(test_one_policy(), in1.begin(), in1.end(), in2.cbegin(), in2.cend(),
+ non_const(compare));
+ }
+ }
+}
+
+int
+main()
+{
+
+ test_includes<float64_t, float64_t>(std::less<>());
+ test_includes<Num<int64_t>, Num<int32_t>>([](const Num<int64_t>& x, const Num<int32_t>& y) { return x < y; });
+ std::cout << done() << std::endl;
+
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/set.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/set.pass.cpp
new file mode 100644
index 00000000000000..40ae3d92c8a03e
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/alg.set.operations/set.pass.cpp
@@ -0,0 +1,280 @@
+// -*- C++ -*-
+//===-- set.pass.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+#include <execution>
+#include <functional>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct Num
+{
+ T val;
+
+ Num() : val{} {}
+ Num(const T& v) : val(v) {}
+
+ //for "includes" checks
+ template <typename T1>
+ bool
+ operator<(const Num<T1>& v1) const
+ {
+ return val < v1.val;
+ }
+
+ //The types Type1 and Type2 must be such that an object of type InputIt can be dereferenced and then implicitly converted to both of them
+ template <typename T1>
+ operator Num<T1>() const
+ {
+ return Num<T1>((T1)val);
+ }
+
+ friend bool
+ operator==(const Num& v1, const Num& v2)
+ {
+ return v1.val == v2.val;
+ }
+};
+
+template <typename Type>
+struct test_set_union
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<!TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
+ Compare comp)
+ {
+ using T1 = typename std::iterator_traits<InputIterator1>::value_type;
+
+ auto n1 = std::distance(first1, last1);
+ auto n2 = std::distance(first2, last2);
+ auto n = n1 + n2;
+ Sequence<T1> expect(n);
+ Sequence<T1> out(n);
+
+ auto expect_res = std::set_union(first1, last1, first2, last2, expect.begin(), comp);
+ auto res = std::set_union(exec, first1, last1, first2, last2, out.begin(), comp);
+
+ EXPECT_TRUE(expect_res - expect.begin() == res - out.begin(), "wrong result for set_union");
+ EXPECT_EQ_N(expect.begin(), out.begin(), std::distance(out.begin(), res), "wrong set_union effect");
+ }
+
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&&, InputIterator1, InputIterator1, InputIterator2, InputIterator2, Compare)
+ {
+ }
+};
+
+template <typename Type>
+struct test_set_intersection
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<!TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
+ Compare comp)
+ {
+ using T1 = typename std::iterator_traits<InputIterator1>::value_type;
+
+ auto n1 = std::distance(first1, last1);
+ auto n2 = std::distance(first2, last2);
+ auto n = n1 + n2;
+ Sequence<T1> expect(n);
+ Sequence<T1> out(n);
+
+ auto expect_res = std::set_intersection(first1, last1, first2, last2, expect.begin(), comp);
+ auto res = std::set_intersection(exec, first1, last1, first2, last2, out.begin(), comp);
+
+ EXPECT_TRUE(expect_res - expect.begin() == res - out.begin(), "wrong result for set_intersection");
+ EXPECT_EQ_N(expect.begin(), out.begin(), std::distance(out.begin(), res), "wrong set_intersection effect");
+ }
+
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&&, InputIterator1, InputIterator1, InputIterator2, InputIterator2, Compare)
+ {
+ }
+};
+
+template <typename Type>
+struct test_set_difference
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<!TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
+ Compare comp)
+ {
+ using T1 = typename std::iterator_traits<InputIterator1>::value_type;
+
+ auto n1 = std::distance(first1, last1);
+ auto n2 = std::distance(first2, last2);
+ auto n = n1 + n2;
+ Sequence<T1> expect(n);
+ Sequence<T1> out(n);
+
+ auto expect_res = std::set_difference(first1, last1, first2, last2, expect.begin(), comp);
+ auto res = std::set_difference(exec, first1, last1, first2, last2, out.begin(), comp);
+
+ EXPECT_TRUE(expect_res - expect.begin() == res - out.begin(), "wrong result for set_difference");
+ EXPECT_EQ_N(expect.begin(), out.begin(), std::distance(out.begin(), res), "wrong set_difference effect");
+ }
+
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&&, InputIterator1, InputIterator1, InputIterator2, InputIterator2, Compare)
+ {
+ }
+};
+
+template <typename Type>
+struct test_set_symmetric_difference
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<!TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2,
+ Compare comp)
+ {
+ using T1 = typename std::iterator_traits<InputIterator1>::value_type;
+
+ auto n1 = std::distance(first1, last1);
+ auto n2 = std::distance(first2, last2);
+ auto n = n1 + n2;
+ Sequence<T1> expect(n);
+ Sequence<T1> out(n);
+
+ auto expect_res = std::set_symmetric_difference(first1, last1, first2, last2, expect.begin(), comp);
+ auto res = std::set_symmetric_difference(exec, first1, last1, first2, last2, out.begin(), comp);
+
+ EXPECT_TRUE(expect_res - expect.begin() == res - out.begin(), "wrong result for set_symmetric_difference");
+ EXPECT_EQ_N(expect.begin(), out.begin(), std::distance(out.begin(), res),
+ "wrong set_symmetric_difference effect");
+ }
+
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename Compare>
+ typename std::enable_if<TestUtils::isReverse<InputIterator1>::value, void>::type
+ operator()(Policy&&, InputIterator1, InputIterator1, InputIterator2, InputIterator2, Compare)
+ {
+ }
+};
+
+template <typename T1, typename T2, typename Compare>
+void
+test_set(Compare compare)
+{
+
+ const std::size_t n_max = 100000;
+
+ // The rand()%(2*n+1) encourages generation of some duplicates.
+ std::srand(4200);
+
+ for (std::size_t n = 0; n < n_max; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ for (std::size_t m = 0; m < n_max; m = m <= 16 ? m + 1 : size_t(2.71828 * m))
+ {
+ //prepare the input ranges
+ Sequence<T1> in1(n, [](std::size_t k) { return rand() % (2 * k + 1); });
+ Sequence<T2> in2(m, [m](std::size_t k) { return (m % 2) * rand() + rand() % (k + 1); });
+
+ std::sort(in1.begin(), in1.end(), compare);
+ std::sort(in2.begin(), in2.end(), compare);
+
+ invoke_on_all_policies(test_set_union<T1>(), in1.begin(), in1.end(), in2.cbegin(), in2.cend(),
+ compare);
+
+ invoke_on_all_policies(test_set_intersection<T1>(), in1.begin(), in1.end(), in2.cbegin(), in2.cend(),
+ compare);
+
+ invoke_on_all_policies(test_set_difference<T1>(), in1.begin(), in1.end(), in2.cbegin(), in2.cend(),
+ compare);
+
+ invoke_on_all_policies(test_set_symmetric_difference<T1>(), in1.begin(), in1.end(), in2.cbegin(),
+ in2.cend(), compare);
+ }
+ }
+}
+
+template <typename T>
+struct test_non_const_set_difference
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ set_difference(exec, input_iter, input_iter, input_iter, input_iter, out_iter, non_const(std::less<T>()));
+ }
+};
+
+template <typename T>
+struct test_non_const_set_intersection
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ set_intersection(exec, input_iter, input_iter, input_iter, input_iter, out_iter, non_const(std::less<T>()));
+ }
+};
+
+template <typename T>
+struct test_non_const_set_symmetric_difference
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ set_symmetric_difference(exec, input_iter, input_iter, input_iter, input_iter, out_iter,
+ non_const(std::less<T>()));
+ }
+};
+
+template <typename T>
+struct test_non_const_set_union
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ set_union(exec, input_iter, input_iter, input_iter, input_iter, out_iter, non_const(std::less<T>()));
+ }
+};
+
+int
+main()
+{
+
+ test_set<float64_t, float64_t>(std::less<>());
+ test_set<Num<int64_t>, Num<int32_t>>([](const Num<int64_t>& x, const Num<int32_t>& y) { return x < y; });
+
+ test_set<MemoryChecker, MemoryChecker>([](const MemoryChecker& val1, const MemoryChecker& val2) -> bool {
+ return val1.value() < val2.value();
+ });
+ EXPECT_FALSE(MemoryChecker::alive_objects() < 0, "wrong effect from set algorithms: number of ctors calls < num of dtors calls");
+ EXPECT_FALSE(MemoryChecker::alive_objects() > 0, "wrong effect from set algorithms: number of ctors calls > num of dtors calls");
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const_set_difference<int32_t>>());
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const_set_intersection<int32_t>>());
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const_set_symmetric_difference<int32_t>>());
+
+ test_algo_basic_double<int32_t>(run_for_rnd_fw<test_non_const_set_union<int32_t>>());
+
+ std::cout << done() << std::endl;
+
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/is_sorted.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/is_sorted.pass.cpp
new file mode 100644
index 00000000000000..d4c6fe291ed849
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/is_sorted.pass.cpp
@@ -0,0 +1,100 @@
+// -*- C++ -*-
+//===-- is_sorted.pass.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_is_sorted
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, bool exam)
+ {
+ using namespace std;
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+
+ //try random-access iterator
+ bool res = is_sorted(exec, first, last);
+ EXPECT_TRUE(exam == res, "is_sorted wrong result for random-access iterator");
+ auto iexam = is_sorted_until(first, last);
+ auto ires = is_sorted_until(exec, first, last);
+ EXPECT_TRUE(iexam == ires, "is_sorted_until wrong result for random-access iterator");
+
+ //try random-access iterator with a predicate
+ res = is_sorted(exec, first, last, std::less<T>());
+ EXPECT_TRUE(exam == res, "is_sorted wrong result for random-access iterator");
+ iexam = is_sorted_until(first, last, std::less<T>());
+ ires = is_sorted_until(exec, first, last, std::less<T>());
+ EXPECT_TRUE(iexam == ires, "is_sorted_until wrong result for random-access iterator");
+ }
+};
+
+template <typename T>
+void
+test_is_sorted_by_type()
+{
+
+ Sequence<T> in(99999, [](size_t v) -> T { return T(v); }); //fill 0..n
+
+ invoke_on_all_policies(test_is_sorted(), in.begin(), in.end(), std::is_sorted(in.begin(), in.end()));
+ invoke_on_all_policies(test_is_sorted(), in.cbegin(), in.cend(), std::is_sorted(in.begin(), in.end()));
+
+ in[in.size() / 2] = -1;
+ invoke_on_all_policies(test_is_sorted(), in.begin(), in.end(), std::is_sorted(in.begin(), in.end()));
+ invoke_on_all_policies(test_is_sorted(), in.cbegin(), in.cend(), std::is_sorted(in.begin(), in.end()));
+
+ in[1] = -1;
+ invoke_on_all_policies(test_is_sorted(), in.begin(), in.end(), std::is_sorted(in.begin(), in.end()));
+ invoke_on_all_policies(test_is_sorted(), in.cbegin(), in.cend(), std::is_sorted(in.begin(), in.end()));
+
+ //an empty container
+ Sequence<T> in0(0);
+ invoke_on_all_policies(test_is_sorted(), in0.begin(), in0.end(), std::is_sorted(in0.begin(), in0.end()));
+ invoke_on_all_policies(test_is_sorted(), in0.cbegin(), in0.cend(), std::is_sorted(in0.begin(), in0.end()));
+
+ //non-descending order
+ Sequence<T> in1(9, [](size_t) -> T { return T(0); });
+ invoke_on_all_policies(test_is_sorted(), in1.begin(), in1.end(), std::is_sorted(in1.begin(), in1.end()));
+ invoke_on_all_policies(test_is_sorted(), in1.cbegin(), in1.cend(), std::is_sorted(in1.begin(), in1.end()));
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ is_sorted(exec, iter, iter, std::less<T>());
+ is_sorted_until(exec, iter, iter, std::less<T>());
+ }
+};
+
+int
+main()
+{
+
+ test_is_sorted_by_type<int32_t>();
+ test_is_sorted_by_type<float64_t>();
+
+ test_is_sorted_by_type<Wrapper<int32_t>>();
+
+ test_algo_basic_single<int32_t>(run_for_rnd_fw<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort.pass.cpp
new file mode 100644
index 00000000000000..bf6e68be8c0790
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort.pass.cpp
@@ -0,0 +1,149 @@
+// -*- C++ -*-
+//===-- partial_sort.pass.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <cmath>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+static std::atomic<int32_t> count_val;
+static std::atomic<int32_t> count_comp;
+
+template <typename T>
+struct Num
+{
+ T val;
+
+ Num() { ++count_val; }
+ Num(T v) : val(v) { ++count_val; }
+ Num(const Num<T>& v) : val(v.val) { ++count_val; }
+ Num(Num<T>&& v) : val(v.val) { ++count_val; }
+ ~Num() { --count_val; }
+ Num<T>&
+ operator=(const Num<T>& v)
+ {
+ val = v.val;
+ return *this;
+ }
+ operator T() const { return val; }
+ bool
+ operator<(const Num<T>& v) const
+ {
+ ++count_comp;
+ return val < v.val;
+ }
+};
+
+struct test_brick_partial_sort
+{
+ template <typename Policy, typename InputIterator, typename Compare>
+ typename std::enable_if<is_same_iterator_category<InputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, InputIterator first, InputIterator last, InputIterator exp_first, InputIterator exp_last,
+ Compare compare)
+ {
+
+ typedef typename std::iterator_traits<InputIterator>::value_type T;
+
+ // The rand()%(2*n+1) encourages generation of some duplicates.
+ std::srand(42);
+ const std::size_t n = last - first;
+ for (std::size_t k = 0; k < n; ++k)
+ {
+ first[k] = T(rand() % (2 * n + 1));
+ }
+ std::copy(first, last, exp_first);
+
+ for (std::size_t p = 0; p < n; p = p <= 16 ? p + 1 : std::size_t(31.415 * p))
+ {
+ auto m1 = first + p;
+ auto m2 = exp_first + p;
+
+ std::partial_sort(exp_first, m2, exp_last, compare);
+ count_comp = 0;
+ std::partial_sort(exec, first, m1, last, compare);
+ EXPECT_EQ_N(exp_first, first, p, "wrong effect from partial_sort");
+
+ //checking upper bound number of comparisons; O(p*(last-first)log(middle-first)); where p - number of threads;
+ if (m1 - first > 1)
+ {
+#ifdef _DEBUG
+# if defined(_PSTL_PAR_BACKEND_TBB)
+ auto p = tbb::this_task_arena::max_concurrency();
+# else
+ auto p = 1;
+# endif
+ auto complex = std::ceil(n * std::log(float32_t(m1 - first)));
+ if (count_comp > complex * p)
+ {
+ std::cout << "complexity exceeded" << std::endl;
+ }
+#endif // _DEBUG
+ }
+ }
+ }
+
+ template <typename Policy, typename InputIterator, typename Compare>
+ typename std::enable_if<!is_same_iterator_category<InputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&&, InputIterator, InputIterator, InputIterator, InputIterator, Compare)
+ {
+ }
+};
+
+template <typename T, typename Compare>
+void
+test_partial_sort(Compare compare)
+{
+
+ const std::size_t n_max = 100000;
+ Sequence<T> in(n_max);
+ Sequence<T> exp(n_max);
+ for (std::size_t n = 0; n < n_max; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ invoke_on_all_policies(test_brick_partial_sort(), in.begin(), in.begin() + n, exp.begin(), exp.begin() + n,
+ compare);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ partial_sort(exec, iter, iter, iter, non_const(std::less<T>()));
+ }
+};
+
+int
+main()
+{
+ count_val = 0;
+
+ test_partial_sort<Num<float32_t>>([](Num<float32_t> x, Num<float32_t> y) { return x < y; });
+
+ EXPECT_TRUE(count_val == 0, "cleanup error");
+
+ test_partial_sort<int32_t>(
+ [](int32_t x, int32_t y) { return x > y; }); // Reversed so accidental use of < will be detected.
+
+ test_algo_basic_single<int32_t>(run_for_rnd<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort_copy.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort_copy.pass.cpp
new file mode 100644
index 00000000000000..9090f89e43616c
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/partial_sort_copy.pass.cpp
@@ -0,0 +1,196 @@
+// -*- C++ -*-
+//===-- partial_sort_copy.pass.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for partial_sort_copy
+#include "support/pstl_test_config.h"
+
+#include <cmath>
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct Num
+{
+ T val;
+
+ Num() : val(0) {}
+ Num(T v) : val(v) {}
+ Num(const Num<T>& v) : val(v.val) {}
+ Num(Num<T>&& v) : val(v.val) {}
+ Num<T>&
+ operator=(const Num<T>& v)
+ {
+ val = v.val;
+ return *this;
+ }
+ operator T() const { return val; }
+ bool
+ operator<(const Num<T>& v) const
+ {
+ return val < v.val;
+ }
+};
+
+template <typename RandomAccessIterator>
+struct test_one_policy
+{
+ RandomAccessIterator d_first;
+ RandomAccessIterator d_last;
+ RandomAccessIterator exp_first;
+ RandomAccessIterator exp_last;
+ // This ctor is needed because output shouldn't be transformed to any iterator type (only random access iterators are allowed)
+ test_one_policy(RandomAccessIterator b1, RandomAccessIterator e1, RandomAccessIterator b2, RandomAccessIterator e2)
+ : d_first(b1), d_last(e1), exp_first(b2), exp_last(e2)
+ {
+ }
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename InputIterator, typename Size, typename T, typename Compare>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, Size n1, Size n2,
+ const T& trash, Compare compare)
+ {
+ }
+
+ template <typename InputIterator, typename Size, typename T, typename Compare>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last, Size n1, Size n2,
+ const T& trash, Compare compare)
+ {
+ }
+
+ template <typename InputIterator, typename Size, typename T>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, Size n1, Size n2,
+ const T& trash)
+ {
+ }
+
+ template <typename InputIterator, typename Size, typename T>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last, Size n1, Size n2,
+ const T& trash)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename Size, typename T, typename Compare>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, Size n1, Size n2, const T& trash,
+ Compare compare)
+ {
+ prepare_data(first, last, n1, trash);
+ RandomAccessIterator exp = std::partial_sort_copy(first, last, exp_first, exp_last, compare);
+ RandomAccessIterator res = std::partial_sort_copy(exec, first, last, d_first, d_last, compare);
+
+ EXPECT_TRUE((exp - exp_first) == (res - d_first), "wrong result from partial_sort_copy with predicate");
+ EXPECT_EQ_N(exp_first, d_first, n2, "wrong effect from partial_sort_copy with predicate");
+ }
+
+ template <typename Policy, typename InputIterator, typename Size, typename T>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, Size n1, Size n2, const T& trash)
+ {
+ prepare_data(first, last, n1, trash);
+ RandomAccessIterator exp = std::partial_sort_copy(first, last, exp_first, exp_last);
+ RandomAccessIterator res = std::partial_sort_copy(exec, first, last, d_first, d_last);
+
+ EXPECT_TRUE((exp - exp_first) == (res - d_first), "wrong result from partial_sort_copy without predicate");
+ EXPECT_EQ_N(exp_first, d_first, n2, "wrong effect from partial_sort_copy without predicate");
+ }
+
+ private:
+ template <typename InputIterator, typename Size, typename T>
+ void
+ prepare_data(InputIterator first, InputIterator last, Size n1, const T& trash)
+ {
+ // The rand()%(2*n+1) encourages generation of some duplicates.
+ std::srand(42);
+ std::generate(first, last, [n1]() { return T(rand() % (2 * n1 + 1)); });
+
+ std::fill(exp_first, exp_last, trash);
+ std::fill(d_first, d_last, trash);
+ }
+};
+
+template <typename T, typename Compare>
+void
+test_partial_sort_copy(Compare compare)
+{
+
+ typedef typename Sequence<T>::iterator iterator_type;
+ const std::size_t n_max = 100000;
+ Sequence<T> in(n_max);
+ Sequence<T> out(2 * n_max);
+ Sequence<T> exp(2 * n_max);
+ std::size_t n1 = 0;
+ std::size_t n2;
+ T trash = T(-666);
+ for (; n1 < n_max; n1 = n1 <= 16 ? n1 + 1 : size_t(3.1415 * n1))
+ {
+ // If both sequences are equal
+ n2 = n1;
+ invoke_on_all_policies(
+ test_one_policy<iterator_type>(out.begin(), out.begin() + n2, exp.begin(), exp.begin() + n2), in.begin(),
+ in.begin() + n1, n1, n2, trash, compare);
+
+ // If first sequence is greater than second
+ n2 = n1 / 3;
+ invoke_on_all_policies(
+ test_one_policy<iterator_type>(out.begin(), out.begin() + n2, exp.begin(), exp.begin() + n2), in.begin(),
+ in.begin() + n1, n1, n2, trash, compare);
+
+ // If first sequence is less than second
+ n2 = 2 * n1;
+ invoke_on_all_policies(
+ test_one_policy<iterator_type>(out.begin(), out.begin() + n2, exp.begin(), exp.begin() + n2), in.begin(),
+ in.begin() + n1, n1, n2, trash, compare);
+ }
+ // Test partial_sort_copy without predicate
+ n1 = n_max;
+ n2 = 2 * n1;
+ invoke_on_all_policies(test_one_policy<iterator_type>(out.begin(), out.begin() + n2, exp.begin(), exp.begin() + n2),
+ in.begin(), in.begin() + n1, n1, n2, trash);
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename InputIterator, typename OutputInterator>
+ void
+ operator()(Policy&& exec, InputIterator input_iter, OutputInterator out_iter)
+ {
+ invoke_if(exec, [&]() {
+ partial_sort_copy(exec, input_iter, input_iter, out_iter, out_iter, non_const(std::less<T>()));
+ });
+ }
+};
+
+int
+main()
+{
+ test_partial_sort_copy<Num<float32_t>>([](Num<float32_t> x, Num<float32_t> y) { return x < y; });
+ test_partial_sort_copy<int32_t>([](int32_t x, int32_t y) { return x > y; });
+
+ test_algo_basic_double<int32_t>(run_for_rnd<test_non_const<int32_t>>());
+
+ test_partial_sort_copy<MemoryChecker>(
+ [](const MemoryChecker& val1, const MemoryChecker& val2){ return val1.value() < val2.value(); });
+ EXPECT_FALSE(MemoryChecker::alive_objects() < 0, "wrong effect from partial_sort_copy: number of ctors calls < num of dtors calls");
+ EXPECT_FALSE(MemoryChecker::alive_objects() > 0, "wrong effect from partial_sort_copy: number of ctors calls > num of dtors calls");
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/algorithms/alg.sorting/sort.pass.cpp b/libcxx/test/std/pstl/algorithms/alg.sorting/sort.pass.cpp
new file mode 100644
index 00000000000000..5617a6019f801f
--- /dev/null
+++ b/libcxx/test/std/pstl/algorithms/alg.sorting/sort.pass.cpp
@@ -0,0 +1,247 @@
+// -*- C++ -*-
+//===-- sort.pass.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <algorithm>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <atomic>
+
+static bool Stable;
+
+//! Number of extant keys
+static std::atomic<int32_t> KeyCount;
+
+//! One more than highest index in array to be sorted.
+static uint32_t LastIndex;
+
+//! Keeping Equal() static and a friend of ParanoidKey class (C++, paragraphs 3.5/7.1.1)
+class ParanoidKey;
+static bool
+Equal(const ParanoidKey& x, const ParanoidKey& y);
+
+//! A key to be sorted, with lots of checking.
+class ParanoidKey
+{
+ //! Value used by comparator
+ int32_t value;
+ //! Original position or special value (Empty or Dead)
+ int32_t index;
+ //! Special value used to mark object without a comparable value, e.g. after being moved from.
+ static const int32_t Empty = -1;
+ //! Special value used to mark destroyed objects.
+ static const int32_t Dead = -2;
+ // True if key object has comparable value
+ bool
+ isLive() const
+ {
+ return (uint32_t)(index) < LastIndex;
+ }
+ // True if key object has been constructed.
+ bool
+ isConstructed() const
+ {
+ return isLive() || index == Empty;
+ }
+
+ public:
+ ParanoidKey()
+ {
+ ++KeyCount;
+ index = Empty;
+ value = Empty;
+ }
+ ParanoidKey(const ParanoidKey& k) : value(k.value), index(k.index)
+ {
+ EXPECT_TRUE(k.isLive(), "source for copy-constructor is dead");
+ ++KeyCount;
+ }
+ ~ParanoidKey()
+ {
+ EXPECT_TRUE(isConstructed(), "double destruction");
+ index = Dead;
+ --KeyCount;
+ }
+ ParanoidKey&
+ operator=(const ParanoidKey& k)
+ {
+ EXPECT_TRUE(k.isLive(), "source for copy-assignment is dead");
+ EXPECT_TRUE(isConstructed(), "destination for copy-assignment is dead");
+ value = k.value;
+ index = k.index;
+ return *this;
+ }
+ ParanoidKey(int32_t index, int32_t value, OddTag) : value(value), index(index) {}
+ ParanoidKey(ParanoidKey&& k) : value(k.value), index(k.index)
+ {
+ EXPECT_TRUE(k.isConstructed(), "source for move-construction is dead");
+// std::stable_sort() fails in move semantics on paranoid test before VS2015
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+ k.index = Empty;
+#endif
+ ++KeyCount;
+ }
+ ParanoidKey&
+ operator=(ParanoidKey&& k)
+ {
+ EXPECT_TRUE(k.isConstructed(), "source for move-assignment is dead");
+ EXPECT_TRUE(isConstructed(), "destination for move-assignment is dead");
+ value = k.value;
+ index = k.index;
+// std::stable_sort() fails in move semantics on paranoid test before VS2015
+#if !defined(_MSC_VER) || _MSC_VER >= 1900
+ k.index = Empty;
+#endif
+ return *this;
+ }
+ friend class KeyCompare;
+ friend bool
+ Equal(const ParanoidKey& x, const ParanoidKey& y);
+};
+
+class KeyCompare
+{
+ enum statusType
+ {
+ //! Special value used to mark defined object.
+ Live = 0xabcd,
+ //! Special value used to mark destroyed objects.
+ Dead = -1
+ } status;
+
+ public:
+ KeyCompare(OddTag) : status(Live) {}
+ ~KeyCompare() { status = Dead; }
+ bool
+ operator()(const ParanoidKey& j, const ParanoidKey& k) const
+ {
+ EXPECT_TRUE(status == Live, "key comparison object not defined");
+ EXPECT_TRUE(j.isLive(), "first key to operator() is not live");
+ EXPECT_TRUE(k.isLive(), "second key to operator() is not live");
+ return j.value < k.value;
+ }
+};
+
+// Equal is equality comparison used for checking result of sort against expected result.
+static bool
+Equal(const ParanoidKey& x, const ParanoidKey& y)
+{
+ return (x.value == y.value && !Stable) || (x.index == y.index);
+}
+
+static bool
+Equal(float32_t x, float32_t y)
+{
+ return x == y;
+}
+
+static bool
+Equal(int32_t x, int32_t y)
+{
+ return x == y;
+}
+
+struct test_sort_with_compare
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Compare>
+ typename std::enable_if<is_same_iterator_category<InputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&& exec, OutputIterator tmp_first, OutputIterator tmp_last, OutputIterator2 expected_first,
+ OutputIterator2 expected_last, InputIterator first, InputIterator, Size n, Compare compare)
+ {
+ using namespace std;
+ copy_n(first, n, expected_first);
+ copy_n(first, n, tmp_first);
+ if (Stable)
+ std::stable_sort(expected_first + 1, expected_last - 1, compare);
+ else
+ std::sort(expected_first + 1, expected_last - 1, compare);
+ int32_t count0 = KeyCount;
+ if (Stable)
+ stable_sort(exec, tmp_first + 1, tmp_last - 1, compare);
+ else
+ sort(exec, tmp_first + 1, tmp_last - 1, compare);
+
+ for (size_t i = 0; i < n; ++i, ++expected_first, ++tmp_first)
+ {
+ // Check that expected[i] is equal to tmp[i]
+ EXPECT_TRUE(Equal(*expected_first, *tmp_first), "bad sort");
+ }
+ int32_t count1 = KeyCount;
+ EXPECT_EQ(count0, count1, "key cleanup error");
+ }
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename OutputIterator2, typename Size,
+ typename Compare>
+ typename std::enable_if<!is_same_iterator_category<InputIterator, std::random_access_iterator_tag>::value,
+ void>::type
+ operator()(Policy&&, OutputIterator, OutputIterator, OutputIterator2, OutputIterator2, InputIterator, InputIterator,
+ Size, Compare)
+ {
+ }
+};
+
+template <typename T, typename Compare, typename Convert>
+void
+test_sort(Compare compare, Convert convert)
+{
+ for (size_t n = 0; n < 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ LastIndex = n + 2;
+ // The rand()%(2*n+1) encourages generation of some duplicates.
+ // Sequence is padded with an extra element at front and back, to detect overwrite bugs.
+ Sequence<T> in(n + 2, [=](size_t k) { return convert(k, rand() % (2 * n + 1)); });
+ Sequence<T> expected(in);
+ Sequence<T> tmp(in);
+ invoke_on_all_policies(test_sort_with_compare(), tmp.begin(), tmp.end(), expected.begin(), expected.end(),
+ in.begin(), in.end(), in.size(), compare);
+ }
+}
+
+template <typename T>
+struct test_non_const
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator iter)
+ {
+ sort(exec, iter, iter, non_const(std::less<T>()));
+ stable_sort(exec, iter, iter, non_const(std::less<T>()));
+ }
+};
+
+int
+main()
+{
+ std::srand(42);
+ for (int32_t kind = 0; kind < 2; ++kind)
+ {
+ Stable = kind != 0;
+ test_sort<ParanoidKey>(KeyCompare(OddTag()),
+ [](size_t k, size_t val) { return ParanoidKey(k, val, OddTag()); });
+ test_sort<float32_t>([](float32_t x, float32_t y) { return x < y; },
+ [](size_t, size_t val) { return float32_t(val); });
+ test_sort<int32_t>(
+ [](int32_t x, int32_t y) { return x > y; }, // Reversed so accidental use of < will be detected.
+ [](size_t, size_t val) { return int32_t(val); });
+ }
+
+ test_algo_basic_single<int32_t>(run_for_rnd<test_non_const<int32_t>>());
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/lit.local.cfg b/libcxx/test/std/pstl/lit.local.cfg
new file mode 100644
index 00000000000000..6b1e2c65790626
--- /dev/null
+++ b/libcxx/test/std/pstl/lit.local.cfg
@@ -0,0 +1,2 @@
+if 'parallel-algorithms' not in config.available_features:
+ config.unsupported = True
diff --git a/libcxx/test/std/pstl/numerics/numeric.ops/adjacent_difference.pass.cpp b/libcxx/test/std/pstl/numerics/numeric.ops/adjacent_difference.pass.cpp
new file mode 100644
index 00000000000000..8e56f61c0ccba2
--- /dev/null
+++ b/libcxx/test/std/pstl/numerics/numeric.ops/adjacent_difference.pass.cpp
@@ -0,0 +1,170 @@
+// -*- C++ -*-
+//===-- adjacent_difference.pass.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <iterator>
+#include <execution>
+#include <numeric>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+template <typename T>
+struct wrapper
+{
+ T t;
+ constexpr explicit wrapper(T t_) : t(t_) {}
+ template <typename T2>
+ constexpr wrapper(const wrapper<T2>& a)
+ {
+ t = a.t;
+ }
+ template <typename T2>
+ constexpr void
+ operator=(const wrapper<T2>& a)
+ {
+ t = a.t;
+ }
+ constexpr wrapper<T>
+ operator-(const wrapper<T>& a) const
+ {
+ return wrapper<T>(t - a.t);
+ }
+};
+
+template <typename T>
+bool
+compare(const T& a, const T& b)
+{
+ return a == b;
+}
+
+template <typename T>
+bool
+compare(const wrapper<T>& a, const wrapper<T>& b)
+{
+ return a.t == b.t;
+}
+
+template <typename Iterator1, typename Iterator2, typename T, typename Function>
+typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
+compute_and_check(Iterator1 first, Iterator1 last, Iterator2 d_first, T, Function f)
+{
+ using T2 = typename std::iterator_traits<Iterator2>::value_type;
+
+ if (first == last)
+ return true;
+
+ {
+ T2 temp(*first);
+ if (!compare(temp, *d_first))
+ return false;
+ }
+ Iterator1 second = std::next(first);
+
+ ++d_first;
+ for (; second != last; ++first, ++second, ++d_first)
+ {
+ T2 temp(f(*second, *first));
+ if (!compare(temp, *d_first))
+ return false;
+ }
+
+ return true;
+}
+
+// we don't want to check equality here
+// because we can't be sure it will be strictly equal for floating point types
+template <typename Iterator1, typename Iterator2, typename T, typename Function>
+typename std::enable_if<std::is_floating_point<T>::value, bool>::type compute_and_check(Iterator1, Iterator1, Iterator2,
+ T, Function)
+{
+ return true;
+}
+
+struct test_one_policy
+{
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || \
+ defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) // dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator1, typename Iterator2, typename T, typename Function>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::unsequenced_policy, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b,
+ Iterator2 actual_e, T trash, Function f)
+ {
+ }
+ template <typename Iterator1, typename Iterator2, typename T, typename Function>
+ typename std::enable_if<is_same_iterator_category<Iterator1, std::random_access_iterator_tag>::value, void>::type
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b,
+ Iterator2 actual_e, T trash, Function f)
+ {
+ }
+#endif
+
+ template <typename ExecutionPolicy, typename Iterator1, typename Iterator2, typename T, typename Function>
+ void
+ operator()(ExecutionPolicy&& exec, Iterator1 data_b, Iterator1 data_e, Iterator2 actual_b, Iterator2 actual_e,
+ T trash, Function f)
+ {
+ using namespace std;
+ using T2 = typename std::iterator_traits<Iterator1>::value_type;
+
+ fill(actual_b, actual_e, trash);
+
+ Iterator2 actual_return = adjacent_difference(exec, data_b, data_e, actual_b);
+ EXPECT_TRUE(compute_and_check(data_b, data_e, actual_b, T2(0), std::minus<T2>()),
+ "wrong effect of adjacent_difference");
+ EXPECT_TRUE(actual_return == actual_e, "wrong result of adjacent_difference");
+
+ fill(actual_b, actual_e, trash);
+
+ actual_return = adjacent_difference(exec, data_b, data_e, actual_b, f);
+ EXPECT_TRUE(compute_and_check(data_b, data_e, actual_b, T2(0), f),
+ "wrong effect of adjacent_difference with functor");
+ EXPECT_TRUE(actual_return == actual_e, "wrong result of adjacent_difference with functor");
+ }
+};
+
+template <typename T1, typename T2, typename Pred>
+void
+test(Pred pred)
+{
+ const std::size_t max_len = 100000;
+
+ static constexpr T2 value = T2(77);
+ static constexpr T1 trash = T1(31);
+
+ Sequence<T1> actual(max_len, [](std::size_t i) { return T1(i); });
+
+ Sequence<T2> data(max_len, [](std::size_t i) { return i % 3 == 2 ? T2(i * i) : value; });
+
+ for (std::size_t len = 0; len < max_len; len = len <= 16 ? len + 1 : std::size_t(3.1415 * len))
+ {
+ invoke_on_all_policies(test_one_policy(), data.begin(), data.begin() + len, actual.begin(),
+ actual.begin() + len, trash, pred);
+ invoke_on_all_policies(test_one_policy(), data.cbegin(), data.cbegin() + len, actual.begin(),
+ actual.begin() + len, trash, pred);
+ }
+}
+
+int
+main()
+{
+ test<uint8_t, uint32_t>([](uint32_t a, uint32_t b) { return a - b; });
+ test<int32_t, int64_t>([](int64_t a, int64_t b) { return a / (b + 1); });
+ test<int64_t, float32_t>([](float32_t a, float32_t b) { return (a + b) / 2; });
+ test<wrapper<int32_t>, wrapper<int64_t>>(
+ [](const wrapper<int64_t>& a, const wrapper<int64_t>& b) { return a - b; });
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/numerics/numeric.ops/reduce.pass.cpp b/libcxx/test/std/pstl/numerics/numeric.ops/reduce.pass.cpp
new file mode 100644
index 00000000000000..b2144b96f5ddbb
--- /dev/null
+++ b/libcxx/test/std/pstl/numerics/numeric.ops/reduce.pass.cpp
@@ -0,0 +1,114 @@
+// -*- C++ -*-
+//===-- reduce.pass.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <numeric>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_long_forms_for_one_policy
+{
+ template <typename Policy, typename Iterator, typename T, typename BinaryOp>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, T init, BinaryOp binary, T expected)
+ {
+ T result_r = std::reduce(exec, first, last, init, binary);
+ EXPECT_EQ(expected, result_r, "bad result from reduce(exec, first, last, init, binary_op)");
+ }
+};
+
+template <typename T, typename BinaryOp, typename F>
+void
+test_long_form(T init, BinaryOp binary_op, F f)
+{
+ // Try sequences of various lengths
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ T expected(init);
+ Sequence<T> in(n, [n, f](size_t k) { return f((int32_t(k ^ n) % 1000 - 500)); });
+ for (size_t k = 0; k < n; ++k)
+ expected = binary_op(expected, in[k]);
+
+ using namespace std;
+
+ T result = transform_reduce_serial(in.cfbegin(), in.cfend(), init, binary_op, [](const T& t) { return t; });
+ EXPECT_EQ(expected, result, "bad result from reduce(first, last, init, binary_op_op)");
+
+ invoke_on_all_policies(test_long_forms_for_one_policy(), in.begin(), in.end(), init, binary_op, expected);
+ invoke_on_all_policies(test_long_forms_for_one_policy(), in.cbegin(), in.cend(), init, binary_op, expected);
+ }
+}
+
+struct test_two_short_forms
+{
+
+#if defined(_PSTL_ICC_16_VC14_TEST_PAR_TBB_RT_RELEASE_64_BROKEN) //dummy specialization by policy type, in case of broken configuration
+ template <typename Iterator>
+ void
+ operator()(pstl::execution::parallel_policy, Iterator first, Iterator last, Sum init, Sum expected)
+ {
+ }
+ template <typename Iterator>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, Iterator first, Iterator last, Sum init, Sum expected)
+ {
+ }
+#endif
+
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, Sum init, Sum expected)
+ {
+ using namespace std;
+
+ Sum r0 = init + reduce(exec, first, last);
+ EXPECT_EQ(expected, r0, "bad result from reduce(exec, first, last)");
+
+ Sum r1 = reduce(exec, first, last, init);
+ EXPECT_EQ(expected, r1, "bad result from reduce(exec, first, last, init)");
+ }
+};
+
+// Test forms of reduce(...) that omit the binary_op or init operands.
+void
+test_short_forms()
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sum init(42, OddTag());
+ Sum expected(init);
+ Sequence<Sum> in(n, [n](size_t k) { return Sum((int32_t(k ^ n) % 1000 - 500), OddTag()); });
+ for (size_t k = 0; k < n; ++k)
+ expected = expected + in[k];
+ invoke_on_all_policies(test_two_short_forms(), in.begin(), in.end(), init, expected);
+ invoke_on_all_policies(test_two_short_forms(), in.cbegin(), in.cend(), init, expected);
+ }
+}
+
+int
+main()
+{
+ // Test for popular types
+ test_long_form(42, std::plus<int32_t>(), [](int32_t x) { return x; });
+ test_long_form(42.0, std::plus<float64_t>(), [](float64_t x) { return x; });
+
+ // Test for strict types
+ test_long_form<Number>(Number(42, OddTag()), Add(OddTag()), [](int32_t x) { return Number(x, OddTag()); });
+
+ // Short forms are just facade for long forms, so just test with a single type.
+ test_short_forms();
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/numerics/numeric.ops/scan.fail.cpp b/libcxx/test/std/pstl/numerics/numeric.ops/scan.fail.cpp
new file mode 100644
index 00000000000000..b17944ae084644
--- /dev/null
+++ b/libcxx/test/std/pstl/numerics/numeric.ops/scan.fail.cpp
@@ -0,0 +1,28 @@
+// -*- C++ -*-
+//===-- scan.fail.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include <execution>
+#include <numeric>
+
+struct CustomPolicy
+{
+} policy;
+
+int32_t
+main()
+{
+ int *first = nullptr, *last = nullptr, *result = nullptr;
+
+ std::exclusive_scan(policy, first, last, result, 0); // expected-error {{no matching function for call to 'exclusive_scan'}}
+ std::exclusive_scan(policy, first, last, result, 0, std::plus<int>()); // expected-error {{no matching function for call to 'exclusive_scan'}}
+
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/numerics/numeric.ops/scan.pass.cpp b/libcxx/test/std/pstl/numerics/numeric.ops/scan.pass.cpp
new file mode 100644
index 00000000000000..e89edc71a2dd0f
--- /dev/null
+++ b/libcxx/test/std/pstl/numerics/numeric.ops/scan.pass.cpp
@@ -0,0 +1,201 @@
+// -*- C++ -*-
+//===-- scan.pass.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <numeric>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// We provide the no execution policy versions of the exclusive_scan and inclusive_scan due checking correctness result of the versions with execution policies.
+//TODO: to add a macro for availability of ver implementations
+template <class InputIterator, class OutputIterator, class T>
+OutputIterator
+exclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, T init)
+{
+ for (; first != last; ++first, ++result)
+ {
+ *result = init;
+ init = init + *first;
+ }
+ return result;
+}
+
+template <class InputIterator, class OutputIterator, class T, class BinaryOperation>
+OutputIterator
+exclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, T init, BinaryOperation binary_op)
+{
+ for (; first != last; ++first, ++result)
+ {
+ *result = init;
+ init = binary_op(init, *first);
+ }
+ return result;
+}
+
+// Note: N4582 is missing the ", class T". Issue was reported 2016-Apr-11 to cxxeditor at gmail.com
+template <class InputIterator, class OutputIterator, class BinaryOperation, class T>
+OutputIterator
+inclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op, T init)
+{
+ for (; first != last; ++first, ++result)
+ {
+ init = binary_op(init, *first);
+ *result = init;
+ }
+ return result;
+}
+
+template <class InputIterator, class OutputIterator, class BinaryOperation>
+OutputIterator
+inclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op)
+{
+ if (first != last)
+ {
+ auto tmp = *first;
+ *result = tmp;
+ return inclusive_scan_serial(++first, last, ++result, binary_op, tmp);
+ }
+ else
+ {
+ return result;
+ }
+}
+
+template <class InputIterator, class OutputIterator>
+OutputIterator
+inclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result)
+{
+ typedef typename std::iterator_traits<InputIterator>::value_type input_type;
+ return inclusive_scan_serial(first, last, result, std::plus<input_type>());
+}
+
+// Most of the framework required for testing inclusive and exclusive scan is identical,
+// so the tests for both are in this file. Which is being tested is controlled by the global
+// flag inclusive, which is set to each alternative by main().
+static bool inclusive;
+
+template <typename Iterator, typename Size, typename T>
+void
+check_and_reset(Iterator expected_first, Iterator out_first, Size n, T trash)
+{
+ EXPECT_EQ_N(expected_first, out_first, n,
+ inclusive ? "wrong result from inclusive_scan" : "wrong result from exclusive_scan");
+ std::fill_n(out_first, n, trash);
+}
+
+struct test_scan_with_plus
+{
+ template <typename Policy, typename Iterator1, typename Iterator2, typename Iterator3, typename Size, typename T>
+ void
+ operator()(Policy&& exec, Iterator1 in_first, Iterator1 in_last, Iterator2 out_first, Iterator2 out_last,
+ Iterator3 expected_first, Iterator3, Size n, T init, T trash)
+ {
+ using namespace std;
+
+ auto orr1 = inclusive ? inclusive_scan_serial(in_first, in_last, expected_first)
+ : exclusive_scan_serial(in_first, in_last, expected_first, init);
+ (void)orr1;
+ auto orr = inclusive ? inclusive_scan(exec, in_first, in_last, out_first)
+ : exclusive_scan(exec, in_first, in_last, out_first, init);
+ EXPECT_TRUE(out_last == orr,
+ inclusive ? "inclusive_scan returned wrong iterator" : "exclusive_scan returned wrong iterator");
+
+ check_and_reset(expected_first, out_first, n, trash);
+ fill(out_first, out_last, trash);
+ }
+};
+
+template <typename T, typename Convert>
+void
+test_with_plus(T init, T trash, Convert convert)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, convert);
+ Sequence<T> expected(in);
+ Sequence<T> out(n, [&](int32_t) { return trash; });
+
+ invoke_on_all_policies(test_scan_with_plus(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), in.size(), init, trash);
+ invoke_on_all_policies(test_scan_with_plus(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), in.size(), init, trash);
+ }
+}
+struct test_scan_with_binary_op
+{
+ template <typename Policy, typename Iterator1, typename Iterator2, typename Iterator3, typename Size, typename T,
+ typename BinaryOp>
+ typename std::enable_if<!TestUtils::isReverse<Iterator1>::value, void>::type
+ operator()(Policy&& exec, Iterator1 in_first, Iterator1 in_last, Iterator2 out_first, Iterator2 out_last,
+ Iterator3 expected_first, Iterator3, Size n, T init, BinaryOp binary_op, T trash)
+ {
+ using namespace std;
+
+ auto orr1 = inclusive ? inclusive_scan_serial(in_first, in_last, expected_first, binary_op, init)
+ : exclusive_scan_serial(in_first, in_last, expected_first, init, binary_op);
+ (void)orr1;
+ auto orr = inclusive ? inclusive_scan(exec, in_first, in_last, out_first, binary_op, init)
+ : exclusive_scan(exec, in_first, in_last, out_first, init, binary_op);
+
+ EXPECT_TRUE(out_last == orr, "scan returned wrong iterator");
+ check_and_reset(expected_first, out_first, n, trash);
+ }
+
+ template <typename Policy, typename Iterator1, typename Iterator2, typename Iterator3, typename Size, typename T,
+ typename BinaryOp>
+ typename std::enable_if<TestUtils::isReverse<Iterator1>::value, void>::type
+ operator()(Policy&&, Iterator1, Iterator1, Iterator2, Iterator2, Iterator3, Iterator3, Size, T, BinaryOp, T)
+ {
+ }
+};
+
+template <typename In, typename Out, typename BinaryOp>
+void
+test_matrix(Out init, BinaryOp binary_op, Out trash)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<In> in(n, [](size_t k) { return In(k, k + 1); });
+
+ Sequence<Out> out(n, [&](size_t) { return trash; });
+ Sequence<Out> expected(n, [&](size_t) { return trash; });
+
+ invoke_on_all_policies(test_scan_with_binary_op(), in.begin(), in.end(), out.begin(), out.end(),
+ expected.begin(), expected.end(), in.size(), init, binary_op, trash);
+ invoke_on_all_policies(test_scan_with_binary_op(), in.cbegin(), in.cend(), out.begin(), out.end(),
+ expected.begin(), expected.end(), in.size(), init, binary_op, trash);
+ }
+}
+
+int
+main()
+{
+ for (int32_t mode = 0; mode < 2; ++mode)
+ {
+ inclusive = mode != 0;
+#if !defined(_PSTL_ICC_19_TEST_SIMD_UDS_WINDOWS_RELEASE_BROKEN)
+ // Test with highly restricted type and associative but not commutative operation
+ test_matrix<Matrix2x2<int32_t>, Matrix2x2<int32_t>>(Matrix2x2<int32_t>(), multiply_matrix<int32_t>,
+ Matrix2x2<int32_t>(-666, 666));
+#endif
+
+ // Since the implict "+" forms of the scan delegate to the generic forms,
+ // there's little point in using a highly restricted type, so just use double.
+ test_with_plus<float64_t>(inclusive ? 0.0 : -1.0, -666.0,
+ [](uint32_t k) { return float64_t((k % 991 + 1) ^ (k % 997 + 2)); });
+ }
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/numerics/numeric.ops/transform_reduce.pass.cpp b/libcxx/test/std/pstl/numerics/numeric.ops/transform_reduce.pass.cpp
new file mode 100644
index 00000000000000..f9c4538bf79316
--- /dev/null
+++ b/libcxx/test/std/pstl/numerics/numeric.ops/transform_reduce.pass.cpp
@@ -0,0 +1,129 @@
+// -*- C++ -*-
+//===-- transform_reduce.pass.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <numeric>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// Functor for xor-operation for modeling binary operations in inner_product
+class XOR
+{
+ public:
+ template <typename T>
+ T
+ operator()(const T& left, const T& right) const
+ {
+ return left ^ right;
+ }
+};
+
+// Model of User-defined class
+class MyClass
+{
+ public:
+ int32_t my_field;
+ MyClass() { my_field = 0; }
+ MyClass(int32_t in) { my_field = in; }
+ MyClass(const MyClass& in) = default;
+
+ friend MyClass
+ operator+(const MyClass& x, const MyClass& y)
+ {
+ return MyClass(x.my_field + y.my_field);
+ }
+ friend MyClass
+ operator-(const MyClass& x)
+ {
+ return MyClass(-x.my_field);
+ }
+ friend MyClass operator*(const MyClass& x, const MyClass& y)
+ {
+ return MyClass(x.my_field * y.my_field);
+ }
+ friend bool operator==(const MyClass& x, const MyClass& y)
+ {
+ return x.my_field == y.my_field;
+ }
+};
+
+template <typename T>
+void
+CheckResults(const T& expected, const T& in)
+{
+ EXPECT_TRUE(expected == in, "wrong result of transform_reduce");
+}
+
+// We need to check correctness only for "int" (for example) except cases
+// if we have "floating-point type"-specialization
+void
+CheckResults(const float32_t&, const float32_t&)
+{
+}
+
+// Test for different types and operations with different iterators
+struct test_transform_reduce
+{
+ template <typename Policy, typename InputIterator1, typename InputIterator2, typename T, typename BinaryOperation1,
+ typename BinaryOperation2, typename UnaryOp>
+ void
+ operator()(Policy&& exec, InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2,
+ T init, BinaryOperation1 opB1, BinaryOperation2 opB2, UnaryOp opU)
+ {
+
+ auto expectedB = std::inner_product(first1, last1, first2, init, opB1, opB2);
+ auto expectedU = transform_reduce_serial(first1, last1, init, opB1, opU);
+ T resRA = std::transform_reduce(exec, first1, last1, first2, init, opB1, opB2);
+ CheckResults(expectedB, resRA);
+ resRA = std::transform_reduce(exec, first1, last1, init, opB1, opU);
+ CheckResults(expectedU, resRA);
+ }
+};
+
+template <typename T, typename BinaryOperation1, typename BinaryOperation2, typename UnaryOp, typename Initializer>
+void
+test_by_type(T init, BinaryOperation1 opB1, BinaryOperation2 opB2, UnaryOp opU, Initializer initObj)
+{
+
+ std::size_t maxSize = 100000;
+ Sequence<T> in1(maxSize, initObj);
+ Sequence<T> in2(maxSize, initObj);
+
+ for (std::size_t n = 0; n < maxSize; n = n < 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ invoke_on_all_policies(test_transform_reduce(), in1.begin(), in1.begin() + n, in2.begin(), in2.begin() + n,
+ init, opB1, opB2, opU);
+ invoke_on_all_policies(test_transform_reduce(), in1.cbegin(), in1.cbegin() + n, in2.cbegin(), in2.cbegin() + n,
+ init, opB1, opB2, opU);
+ }
+}
+
+int
+main()
+{
+ test_by_type<int32_t>(42, std::plus<int32_t>(), std::multiplies<int32_t>(), std::negate<int32_t>(),
+ [](std::size_t) -> int32_t { return int32_t(rand() % 1000); });
+ test_by_type<int64_t>(0, [](const int64_t& a, const int64_t& b) -> int64_t { return a | b; }, XOR(),
+ [](const int64_t& x) -> int64_t { return x * 2; },
+ [](std::size_t) -> int64_t { return int64_t(rand() % 1000); });
+ test_by_type<float32_t>(
+ 1.0f, std::multiplies<float32_t>(), [](const float32_t& a, const float32_t& b) -> float32_t { return a + b; },
+ [](const float32_t& x) -> float32_t { return x + 2; }, [](std::size_t) -> float32_t { return rand() % 1000; });
+ test_by_type<MyClass>(MyClass(), std::plus<MyClass>(), std::multiplies<MyClass>(), std::negate<MyClass>(),
+ [](std::size_t) -> MyClass { return MyClass(rand() % 1000); });
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/numerics/numeric.ops/transform_scan.pass.cpp b/libcxx/test/std/pstl/numerics/numeric.ops/transform_scan.pass.cpp
new file mode 100644
index 00000000000000..95294e43c4c1d2
--- /dev/null
+++ b/libcxx/test/std/pstl/numerics/numeric.ops/transform_scan.pass.cpp
@@ -0,0 +1,177 @@
+// -*- C++ -*-
+//===-- transform_scan.pass.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <numeric>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// Most of the framework required for testing inclusive and exclusive transform-scans is identical,
+// so the tests for both are in this file. Which is being tested is controlled by the global
+// flag inclusive, which is set to each alternative by main().
+static bool inclusive;
+
+template <typename Iterator, typename Size, typename T>
+void
+check_and_reset(Iterator expected_first, Iterator out_first, Size n, T trash)
+{
+ EXPECT_EQ_N(expected_first, out_first, n,
+ inclusive ? "wrong result from transform_inclusive_scan"
+ : "wrong result from transform_exclusive_scan");
+ std::fill_n(out_first, n, trash);
+}
+
+struct test_transform_scan
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename Size, typename UnaryOp,
+ typename T, typename BinaryOp>
+ typename std::enable_if<!TestUtils::isReverse<InputIterator>::value, void>::type
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first,
+ OutputIterator out_last, OutputIterator expected_first, OutputIterator, Size n, UnaryOp unary_op, T init,
+ BinaryOp binary_op, T trash)
+ {
+ using namespace std;
+
+ auto orr1 =
+ inclusive
+ ? transform_inclusive_scan(std::execution::seq, first, last, expected_first, binary_op, unary_op, init)
+ : transform_exclusive_scan(std::execution::seq, first, last, expected_first, init, binary_op, unary_op);
+ auto orr2 = inclusive ? transform_inclusive_scan(exec, first, last, out_first, binary_op, unary_op, init)
+ : transform_exclusive_scan(exec, first, last, out_first, init, binary_op, unary_op);
+ EXPECT_TRUE(out_last == orr2, "transform...scan returned wrong iterator");
+ check_and_reset(expected_first, out_first, n, trash);
+
+ // Checks inclusive scan if init is not provided
+ if (inclusive && n > 0)
+ {
+ orr1 = transform_inclusive_scan(std::execution::seq, first, last, expected_first, binary_op, unary_op);
+ orr2 = transform_inclusive_scan(exec, first, last, out_first, binary_op, unary_op);
+ EXPECT_TRUE(out_last == orr2, "transform...scan returned wrong iterator");
+ check_and_reset(expected_first, out_first, n, trash);
+ }
+ }
+
+ template <typename Policy, typename InputIterator, typename OutputIterator, typename Size, typename UnaryOp,
+ typename T, typename BinaryOp>
+ typename std::enable_if<TestUtils::isReverse<InputIterator>::value, void>::type
+ operator()(Policy&&, InputIterator, InputIterator, OutputIterator, OutputIterator, OutputIterator, OutputIterator,
+ Size, UnaryOp, T, BinaryOp, T)
+ {
+ }
+};
+
+const uint32_t encryption_mask = 0x314;
+
+template <typename InputIterator, typename OutputIterator, typename UnaryOperation, typename T,
+ typename BinaryOperation>
+std::pair<OutputIterator, T>
+transform_inclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation unary_op,
+ T init, BinaryOperation binary_op) noexcept
+{
+ for (; first != last; ++first, ++result)
+ {
+ init = binary_op(init, unary_op(*first));
+ *result = init;
+ }
+ return std::make_pair(result, init);
+}
+
+template <typename InputIterator, typename OutputIterator, typename UnaryOperation, typename T,
+ typename BinaryOperation>
+std::pair<OutputIterator, T>
+transform_exclusive_scan_serial(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation unary_op,
+ T init, BinaryOperation binary_op) noexcept
+{
+ for (; first != last; ++first, ++result)
+ {
+ *result = init;
+ init = binary_op(init, unary_op(*first));
+ }
+ return std::make_pair(result, init);
+}
+
+template <typename In, typename Out, typename UnaryOp, typename BinaryOp>
+void
+test(UnaryOp unary_op, Out init, BinaryOp binary_op, Out trash)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<In> in(n, [](size_t k) { return In(k ^ encryption_mask); });
+
+ Out tmp = init;
+ Sequence<Out> expected(n, [&](size_t k) -> Out {
+ if (inclusive)
+ {
+ tmp = binary_op(tmp, unary_op(in[k]));
+ return tmp;
+ }
+ else
+ {
+ Out val = tmp;
+ tmp = binary_op(tmp, unary_op(in[k]));
+ return val;
+ }
+ });
+
+ Sequence<Out> out(n, [&](size_t) { return trash; });
+
+ auto result =
+ inclusive
+ ? transform_inclusive_scan_serial(in.cbegin(), in.cend(), out.fbegin(), unary_op, init, binary_op)
+ : transform_exclusive_scan_serial(in.cbegin(), in.cend(), out.fbegin(), unary_op, init, binary_op);
+ (void)result;
+ check_and_reset(expected.begin(), out.begin(), out.size(), trash);
+
+ invoke_on_all_policies(test_transform_scan(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), in.size(), unary_op, init, binary_op, trash);
+ invoke_on_all_policies(test_transform_scan(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), in.size(), unary_op, init, binary_op, trash);
+ }
+}
+
+template <typename In, typename Out, typename UnaryOp, typename BinaryOp>
+void
+test_matrix(UnaryOp unary_op, Out init, BinaryOp binary_op, Out trash)
+{
+ for (size_t n = 0; n <= 100000; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<In> in(n, [](size_t k) { return In(k, k + 1); });
+
+ Sequence<Out> out(n, [&](size_t) { return trash; });
+ Sequence<Out> expected(n, [&](size_t) { return trash; });
+
+ invoke_on_all_policies(test_transform_scan(), in.begin(), in.end(), out.begin(), out.end(), expected.begin(),
+ expected.end(), in.size(), unary_op, init, binary_op, trash);
+ invoke_on_all_policies(test_transform_scan(), in.cbegin(), in.cend(), out.begin(), out.end(), expected.begin(),
+ expected.end(), in.size(), unary_op, init, binary_op, trash);
+ }
+}
+
+int
+main()
+{
+ for (int32_t mode = 0; mode < 2; ++mode)
+ {
+ inclusive = mode != 0;
+#if !defined(_PSTL_ICC_19_TEST_SIMD_UDS_WINDOWS_RELEASE_BROKEN)
+ test_matrix<Matrix2x2<int32_t>, Matrix2x2<int32_t>>([](const Matrix2x2<int32_t> x) { return x; },
+ Matrix2x2<int32_t>(), multiply_matrix<int32_t>,
+ Matrix2x2<int32_t>(-666, 666));
+#endif
+ test<int32_t, uint32_t>([](int32_t x) { return x++; }, -123, [](int32_t x, int32_t y) { return x + y; }, 666);
+ }
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_construct.pass.cpp b/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_construct.pass.cpp
new file mode 100644
index 00000000000000..a5bdbb3a9c7e1a
--- /dev/null
+++ b/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_construct.pass.cpp
@@ -0,0 +1,123 @@
+// -*- C++ -*-
+//===-- uninitialized_construct.pass.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for uninitialized_default_construct, uninitialized_default_construct_n,
+// uninitialized_value_construct, uninitialized_value_construct_n
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <memory>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// function of checking correctness for uninitialized.construct.value
+template <typename T, typename Iterator>
+bool
+IsCheckValueCorrectness(Iterator begin, Iterator end)
+{
+ for (; begin != end; ++begin)
+ {
+ if (*begin != T())
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+struct test_uninit_construct
+{
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator begin, Iterator end, size_t n, /*is_trivial<T>=*/std::false_type)
+ {
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+ // it needs for cleaning memory that was filled by default constructors in unique_ptr<T[]> p(new T[n])
+ // and for cleaning memory after last calling of uninitialized_value_construct_n.
+ // It is important for non-trivial types
+ std::destroy_n(exec, begin, n);
+
+ // reset counter of constructors
+ T::SetCount(0);
+ // run algorithm
+ std::uninitialized_default_construct(exec, begin, end);
+ // compare counter of constructors to length of container
+ EXPECT_TRUE(T::Count() == n, "wrong uninitialized_default_construct");
+ // destroy objects for testing new algorithms on same memory
+ std::destroy(exec, begin, end);
+
+ std::uninitialized_default_construct_n(exec, begin, n);
+ EXPECT_TRUE(T::Count() == n, "wrong uninitialized_default_construct_n");
+ std::destroy_n(exec, begin, n);
+
+ std::uninitialized_value_construct(exec, begin, end);
+ EXPECT_TRUE(T::Count() == n, "wrong uninitialized_value_construct");
+ std::destroy(exec, begin, end);
+
+ std::uninitialized_value_construct_n(exec, begin, n);
+ EXPECT_TRUE(T::Count() == n, "wrong uninitialized_value_construct_n");
+ }
+
+ template <typename Policy, typename Iterator>
+ void
+ operator()(Policy&& exec, Iterator begin, Iterator end, size_t n, /*is_trivial<T>=*/std::true_type)
+ {
+ typedef typename std::iterator_traits<Iterator>::value_type T;
+
+ std::uninitialized_default_construct(exec, begin, end);
+ std::destroy(exec, begin, end);
+
+ std::uninitialized_default_construct_n(exec, begin, n);
+ std::destroy_n(exec, begin, n);
+
+ std::uninitialized_value_construct(exec, begin, end);
+ // check correctness for uninitialized.construct.value
+ EXPECT_TRUE(IsCheckValueCorrectness<T>(begin, end), "wrong uninitialized_value_construct");
+ std::destroy(exec, begin, end);
+
+ std::uninitialized_value_construct_n(exec, begin, n);
+ EXPECT_TRUE(IsCheckValueCorrectness<T>(begin, end), "wrong uninitialized_value_construct_n");
+ std::destroy_n(exec, begin, n);
+ }
+};
+
+template <typename T>
+void
+test_uninit_construct_by_type()
+{
+ std::size_t N = 100000;
+ for (size_t n = 0; n <= N; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ std::unique_ptr<T[]> p(new T[n]);
+ invoke_on_all_policies(test_uninit_construct(), p.get(), std::next(p.get(), n), n, std::is_trivial<T>());
+ }
+}
+
+int
+main()
+{
+
+ // for user-defined types
+#if !defined(_PSTL_ICC_16_VC14_TEST_PAR_TBB_RT_RELEASE_64_BROKEN)
+ test_uninit_construct_by_type<Wrapper<int32_t>>();
+ test_uninit_construct_by_type<Wrapper<std::vector<std::string>>>();
+#endif
+
+ // for trivial types
+ test_uninit_construct_by_type<int8_t>();
+ test_uninit_construct_by_type<float64_t>();
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_copy_move.pass.cpp b/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_copy_move.pass.cpp
new file mode 100644
index 00000000000000..21186b41ee139b
--- /dev/null
+++ b/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_copy_move.pass.cpp
@@ -0,0 +1,143 @@
+// -*- C++ -*-
+//===-- uninitialized_copy_move.pass.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Tests for uninitialized_copy, uninitialized_copy_n, uninitialized_move, uninitialized_move_n
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <memory>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+// function of checking correctness for uninitialized.construct.value
+template <typename InputIterator, typename OutputIterator, typename Size>
+bool
+IsCheckValueCorrectness(InputIterator first1, OutputIterator first2, Size n)
+{
+ for (Size i = 0; i < n; ++i, ++first1, ++first2)
+ {
+ if (*first1 != *first2)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+struct test_uninitialized_copy_move
+{
+ template <typename Policy, typename InputIterator, typename OutputIterator>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first, size_t n,
+ /*is_trivial<T>=*/std::false_type)
+ {
+ typedef typename std::iterator_traits<InputIterator>::value_type T;
+ // it needs for cleaning memory that was filled by default constructors in unique_ptr<T[]> p(new T[n])
+ // and for cleaning memory after last calling of uninitialized_value_construct_n.
+ // It is important for non-trivial types
+ std::destroy_n(exec, out_first, n);
+
+ // reset counter of constructors
+ T::SetCount(0);
+ // run algorithm
+ std::uninitialized_copy(exec, first, last, out_first);
+ // compare counter of constructors to length of container
+ EXPECT_TRUE(T::Count() == n, "wrong uninitialized_copy");
+ // destroy objects for testing new algorithms on same memory
+ std::destroy_n(exec, out_first, n);
+
+ std::uninitialized_copy_n(exec, first, n, out_first);
+ EXPECT_TRUE(T::Count() == n, "wrong uninitialized_copy_n");
+ std::destroy_n(exec, out_first, n);
+
+ // For move
+ std::uninitialized_move(exec, first, last, out_first);
+ // compare counter of constructors to length of container
+ EXPECT_TRUE(T::MoveCount() == n, "wrong uninitialized_move");
+ // destroy objects for testing new algorithms on same memory
+ std::destroy_n(exec, out_first, n);
+
+ std::uninitialized_move_n(exec, first, n, out_first);
+ EXPECT_TRUE(T::MoveCount() == n, "wrong uninitialized_move_n");
+ std::destroy_n(exec, out_first, n);
+ }
+
+#if defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN)
+ template <typename InputIterator, typename OutputIterator>
+ void
+ operator()(pstl::execution::unsequenced_policy, InputIterator first, InputIterator last, OutputIterator out_first,
+ size_t n, /*is_trivial<T>=*/std::true_type)
+ {
+ }
+ template <typename InputIterator, typename OutputIterator>
+ void
+ operator()(pstl::execution::parallel_unsequenced_policy, InputIterator first, InputIterator last,
+ OutputIterator out_first, size_t n, /*is_trivial<T>=*/std::true_type)
+ {
+ }
+#endif
+
+ template <typename Policy, typename InputIterator, typename OutputIterator>
+ void
+ operator()(Policy&& exec, InputIterator first, InputIterator last, OutputIterator out_first, size_t n,
+ /*is_trivial<T>=*/std::true_type)
+ {
+ std::uninitialized_copy(exec, first, last, out_first);
+ EXPECT_TRUE(IsCheckValueCorrectness(first, out_first, n), "wrong uninitialized_copy");
+ std::destroy_n(exec, out_first, n);
+
+ std::uninitialized_copy_n(exec, first, n, out_first);
+ EXPECT_TRUE(IsCheckValueCorrectness(first, out_first, n), "wrong uninitialized_copy_n");
+ std::destroy_n(exec, out_first, n);
+
+ std::uninitialized_move(exec, first, last, out_first);
+ EXPECT_TRUE(IsCheckValueCorrectness(first, out_first, n), "wrong uninitialized_move");
+ std::destroy_n(exec, out_first, n);
+
+ std::uninitialized_move_n(exec, first, n, out_first);
+ EXPECT_TRUE(IsCheckValueCorrectness(first, out_first, n), "wrong uninitialized_move_n");
+ std::destroy_n(exec, out_first, n);
+ }
+};
+
+template <typename T>
+void
+test_uninitialized_copy_move_by_type()
+{
+ std::size_t N = 100000;
+ for (size_t n = 0; n <= N; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ Sequence<T> in(n, [=](size_t k) -> T { return T(k); });
+ std::unique_ptr<T[]> p(new T[n]);
+ invoke_on_all_policies(test_uninitialized_copy_move(), in.begin(), in.end(), p.get(), n, std::is_trivial<T>());
+ }
+}
+
+int
+main()
+{
+
+ // for trivial types
+ test_uninitialized_copy_move_by_type<int16_t>();
+ test_uninitialized_copy_move_by_type<float64_t>();
+
+ // for user-defined types
+#if !defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) && !defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) && \
+ !defined(_PSTL_ICC_16_VC14_TEST_PAR_TBB_RT_RELEASE_64_BROKEN)
+ test_uninitialized_copy_move_by_type<Wrapper<int8_t>>();
+#endif
+
+ std::cout << done() << std::endl;
+ return 0;
+}
diff --git a/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp b/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp
new file mode 100644
index 00000000000000..d579ea558839d0
--- /dev/null
+++ b/libcxx/test/std/pstl/utilities/memory/specialized.algorithms/uninitialized_fill_destroy.pass.cpp
@@ -0,0 +1,93 @@
+// -*- C++ -*-
+//===-- uninitialized_fill_destroy.pass.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include "support/pstl_test_config.h"
+
+#include <execution>
+#include <memory>
+
+#include "support/utils.h"
+
+using namespace TestUtils;
+
+struct test_uninitialized_fill_destroy
+{
+ template <typename Policy, typename Iterator, typename T>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, const T& in, std::size_t n, std::false_type)
+ {
+ using namespace std;
+ {
+ T::SetCount(0);
+ uninitialized_fill(exec, first, last, in);
+ size_t count = count_if(first, last, [&in](T& x) -> bool { return x == in; });
+ EXPECT_TRUE(n == count, "wrong work of uninitialized_fill");
+ destroy(exec, first, last);
+ EXPECT_TRUE(T::Count() == 0, "wrong work of destroy");
+ }
+
+ {
+ auto res = uninitialized_fill_n(exec, first, n, in);
+ EXPECT_TRUE(res == last, "wrong result of uninitialized_fill_n");
+ size_t count = count_if(first, last, [&in](T& x) -> bool { return x == in; });
+ EXPECT_TRUE(n == count, "wrong work of uninitialized_fill_n");
+ destroy_n(exec, first, n);
+ EXPECT_TRUE(T::Count() == 0, "wrong work of destroy_n");
+ }
+ }
+ template <typename Policy, typename Iterator, typename T>
+ void
+ operator()(Policy&& exec, Iterator first, Iterator last, const T& in, std::size_t n, std::true_type)
+ {
+ using namespace std;
+ {
+ destroy(exec, first, last);
+ uninitialized_fill(exec, first, last, in);
+ size_t count = count_if(first, last, [&in](T& x) -> bool { return x == in; });
+ EXPECT_EQ(n, count, "wrong work of uninitialized:_fill");
+ }
+ {
+ destroy_n(exec, first, n);
+ auto res = uninitialized_fill_n(exec, first, n, in);
+ size_t count = count_if(first, last, [&in](T& x) -> bool { return x == in; });
+ EXPECT_EQ(n, count, "wrong work of uninitialized_fill_n");
+ EXPECT_TRUE(res == last, "wrong result of uninitialized_fill_n");
+ }
+ }
+};
+
+template <typename T>
+void
+test_uninitialized_fill_destroy_by_type()
+{
+ std::size_t N = 100000;
+ for (size_t n = 0; n <= N; n = n <= 16 ? n + 1 : size_t(3.1415 * n))
+ {
+ std::unique_ptr<T[]> p(new T[n]);
+ invoke_on_all_policies(test_uninitialized_fill_destroy(), p.get(), std::next(p.get(), n), T(), n,
+ std::is_trivial<T>());
+ }
+}
+
+int
+main()
+{
+ // for trivial types
+ test_uninitialized_fill_destroy_by_type<int32_t>();
+ test_uninitialized_fill_destroy_by_type<float64_t>();
+
+ // for user-defined types
+ test_uninitialized_fill_destroy_by_type<Wrapper<std::string>>();
+ test_uninitialized_fill_destroy_by_type<Wrapper<int8_t*>>();
+ std::cout << done() << std::endl;
+
+ return 0;
+}
diff --git a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
new file mode 100755
index 00000000000000..ddf73811254f23
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
@@ -0,0 +1,628 @@
+//===- ARMRandezvousShadowStack.cpp - ARM Randezvous Shadow Stack ---------===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of a pass that instruments ARM machine
+// code to save/load the return address to/from a randomized compact shadow
+// stack.
+//
+//===----------------------------------------------------------------------===//
+
+#define DEBUG_TYPE "arm-randezvous-shadow-stack"
+
+#include "ARMEncodeDecode.h"
+#include "ARMRandezvousCLR.h"
+#include "ARMRandezvousOptions.h"
+#include "MCTargetDesc/ARMAddressingModes.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+using namespace llvm;
+
+STATISTIC(NumPrologues, "Number of prologues transformed to use shadow stack");
+STATISTIC(NumEpilogues, "Number of epilogues transformed to use shadow stack");
+STATISTIC(NumNullified, "Number of return addresses nullified");
+
+char ARMEncodeDecode::ID = 0;
+
+ARMEncodeDecode::ARMEncodeDecode() : ModulePass(ID) {}
+
+StringRef ARMEncodeDecode::getPassName() const {
+ return "ARM Encode and Decode Pass";
+}
+
+void ARMEncodeDecode::getAnalysisUsage(AnalysisUsage &AU) const {
+ // We need this to access MachineFunctions
+ AU.addRequired<MachineModuleInfoWrapperPass>();
+
+ AU.setPreservesCFG();
+ ModulePass::getAnalysisUsage(AU);
+}
+
+// 初始化R9
+
+// 代码插桩,如下
+// mov r3 [r4]
+// // Decode the function pointer
+// xor r3 r9
+// // Jump or not jump to the other shadow
+// add r3 #offset (add r3 #0)
+// // Jump to the target function
+// blx r3
+
+// push {r4-r8}
+// // Encode the lr before push it to the stack
+// xor lr r9
+// push lr
+
+// pop {r4-r8} lr
+// // Decode the value of lr after fetch it from the stack
+// xor lr r9
+
+// pop {r7,lr}
+// xor pc lr r9
+
+//
+// Method: createInitFunction()
+//
+// Description:
+// This method creates a function (both Function and MachineFunction) that
+// initializes the reserved registers for the shadow stack.
+//
+// Inputs:
+// M - A reference to the Module in which to create the function.
+// SS - A reference to the shadow stack global variable.
+//
+// Return value:
+// A pointer to the created Function.
+//
+Function *ARMEncodeDecode::createInitFunction(Module &M) {
+ // Create types for the init function
+ LLVMContext &Ctx = M.getContext();
+ FunctionType *FuncTy = FunctionType::get(Type::getVoidTy(Ctx), false);
+
+ // Create the init function
+ FunctionCallee FC =
+ M.getOrInsertFunction(ARMEncodeDecode::InitFuncName, FuncTy);
+ Function *F = dyn_cast<Function>(FC.getCallee());
+ assert(F != nullptr && "Init function has wrong type!");
+ MachineModuleInfo &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+ MachineFunction &MF = MMI.getOrCreateMachineFunction(*F);
+
+ // Set necessary attributes and properties
+ F->setLinkage(GlobalVariable::LinkOnceAnyLinkage);
+ if (!F->hasFnAttribute(Attribute::Naked)) {
+ F->addFnAttr(Attribute::Naked);
+ }
+ if (!F->hasFnAttribute(Attribute::NoUnwind)) {
+ F->addFnAttr(Attribute::NoUnwind);
+ }
+ if (!F->hasFnAttribute(Attribute::WillReturn)) {
+ F->addFnAttr(Attribute::WillReturn);
+ }
+ using Property = MachineFunctionProperties::Property;
+ if (!MF.getProperties().hasProperty(Property::NoVRegs)) {
+ MF.getProperties().set(Property::NoVRegs);
+ }
+
+ // Create a basic block if not created
+ if (F->empty()) {
+ assert(MF.empty() && "Machine IR basic block already there!");
+
+ // Build an IR basic block
+ BasicBlock *BB = BasicBlock::Create(Ctx, "", F);
+ IRBuilder<> IRB(BB);
+ IRB.CreateRetVoid(); // At this point, what the IR basic block contains
+ // doesn't matter so just place a return there
+
+ // Build machine IR basic block(s)
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ MachineBasicBlock *MBB = MF.CreateMachineBasicBlock(BB);
+ // MachineBasicBlock * MBB2 = nullptr;
+ // MachineBasicBlock * MBB3 = nullptr;
+ MachineBasicBlock *RetMBB = MBB;
+ MF.push_back(MBB);
+
+ // // Generate a static random stride
+ // uint64_t Stride = (*RNG)();
+ // Stride &= (1ul << (EncodeDecodeNumberLength - 1)) - 1;
+ // Stride &= ~0x3ul;
+
+ uint64_t Stride = 0x00000001;
+
+ if (ARM_AM::getT2SOImmVal(Stride) != -1) {
+ // Use MOVi if the stride can be encoded in Thumb modified constant
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi), XorReg)
+ .addImm(Stride)
+ .add(predOps(ARMCC::AL))
+ .add(condCodeOp()); // No 'S' bit
+ } else {
+ // Otherwise use MOVi16/MOVTi16 to encode lower/upper 16 bits of the
+ // stride
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVi16), XorReg)
+ .addImm(Stride & 0xffff)
+ .add(predOps(ARMCC::AL));
+ BuildMI(MBB, DebugLoc(), TII->get(ARM::t2MOVTi16), XorReg)
+ .addReg(XorReg)
+ .addImm((Stride >> 16) & 0xffff)
+ .add(predOps(ARMCC::AL));
+ }
+
+ // BX_RET
+ BuildMI(RetMBB, DebugLoc(), TII->get(ARM::tBX_RET)).add(predOps(ARMCC::AL));
+ }
+ // Add the init function to @llvm.used
+ appendToUsed(M, {F});
+
+ return F;
+}
+
+//
+// Method: EncodeLR()
+//
+// Description:
+// This method modifies a PUSH instruction to not save LR to the stack and
+// inserts new instructions that save LR to the shadow stack.
+//
+// Inputs:
+// MI - A reference to a PUSH instruction that saves LR to the stack.
+// LR - A reference to the LR operand of the PUSH.
+// Stride - A static stride to use.
+//
+// Return value:
+// true - The machine code was modified.
+//
+
+// push {r4-r8}
+// // Encode the lr before push it to the stack
+// xor lr r9
+// push lr
+bool ARMEncodeDecode::EncodeLR(MachineInstr &MI, MachineOperand &LR,
+ uint32_t Stride) {
+ MachineFunction &MF = *MI.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc &DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ // 1. replace the old PUSH with a new one that doesn't push LR to the
+ // stack
+ switch (MI.getOpcode()) {
+ case ARM::t2STMDB_UPD:
+ // STMDB_UPD should store at least two registers; if it happens to be two,
+ // we replace it with a STR_PRE
+ assert(MI.getNumExplicitOperands() >= 6 && "Buggy STMDB_UPD!");
+ if (MI.getNumExplicitOperands() > 6) {
+ MI.removeOperand(MI.getOperandNo(&LR));
+ } else {
+ unsigned Idx = MI.getOperandNo(&LR);
+ Idx = Idx == 4 ? 5 : 4;
+ insertInstBefore(MI, BuildMI(MF, DL, TII->get(ARM::t2STR_PRE), ARM::SP)
+ .add(MI.getOperand(Idx))
+ .addReg(ARM::SP)
+ .addImm(-4)
+ .add(predOps(Pred, PredReg))
+ .setMIFlags(MI.getFlags()));
+ removeInst(MI);
+ }
+ break;
+
+ case ARM::tPUSH:
+ // PUSH should store at least one register; if it happens to be one, we
+ // just remove it
+ assert(MI.getNumExplicitOperands() >= 3 && "Buggy PUSH!");
+ if (MI.getNumExplicitOperands() > 3) {
+ MI.removeOperand(MI.getOperandNo(&LR));
+ } else {
+ removeInst(MI);
+ }
+ break;
+
+ // ARM::t2STR_PRE
+ default:
+ // STR_PRE only stores one register, so we just remove it
+ removeInst(MI);
+ break;
+ }
+
+ // 2. Build the xor instruction
+ // eor.w lr,lr,r9
+
+ std::vector<MachineInstr *> NewInsts;
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2EORrr), ARM::LR)
+ .addReg(ARM::LR)
+ .addReg(XorReg)
+ .add(predOps(Pred, PredReg))
+ .add(condCodeOp()));
+
+ // 3. insert a new PUSH with a new one that only push LR to the stack
+ switch (MI.getOpcode()) {
+ case ARM::tPUSH:
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tPUSH))
+ .add(predOps(Pred, PredReg))
+ .addReg(ARM::LR));
+ break;
+
+ // ARM::t2STR_PRE
+ default:
+ // STR_PRE only stores one register, so we just remove it
+ removeInst(MI);
+ break;
+ }
+
+ MI.addOperand(
+ MachineOperand::CreateReg(storeReg, /*isDef=*/true, /*isImp=*/false));
+
+ // 4. Now insert these new instructions into the basic block
+ insertInstsBefore(MI, NewInsts);
+
+ ++NumPrologues;
+ return true;
+}
+
+bool ARMEncodeDecode::EncodeCallSite(MachineInstr &MI, MachineOperand &MO,
+ uint32_t Stride) {
+ MachineFunction &MF = *MI.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc &DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ std::vector<MachineInstr *> NewInsts;
+ unsigned Idx = MI.getOperandNo(&MO);
+
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2EORrr), MO.getReg())
+ .addReg(MO.getReg())
+ .addReg(XorReg)
+ .add(predOps(Pred, PredReg))
+ .add(condCodeOp()));
+
+ // insert the decode instructions before blx
+ insertInstsBefore(MI, NewInsts);
+
+ return true;
+}
+
+//
+// Method: DecodeLR()
+//
+// Description:
+// This method modifies a POP instruction to not write to PC/LR and inserts
+// new instructions that load the return address from the shadow stack into
+// PC/LR.
+//
+// Inputs:
+// MI - A reference to a POP instruction that writes to LR or PC.
+// PCLR - A reference to the PC or LR operand of the POP.
+// Stride - A static stride to use.
+//
+// Return value:
+// true - The machine code was modified.
+//
+
+bool ARMEncodeDecode::DecodeLR(MachineInstr &MI, MachineOperand &PCLR,
+ uint32_t Stride) {
+ MachineFunction &MF = *MI.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc &DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ std::vector<MachineInstr *> NewInsts;
+
+ MachineInstrBuilder MIB =
+ BuildMI(MF, DL, TII->get(ARM::t2LDMIA_UPD), ARM::SP).addReg(ARM::SP);
+ for (MachineOperand &MO : MI.explicit_operands()) {
+ if (MO.isReg() && MO.getReg() == ARM::PC) {
+ MIB.addReg(ARM::LR, RegState::Define);
+ } else {
+ MIB.add(MO);
+ }
+ }
+ NewInsts.push_back(MIB);
+
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::t2EORrr), ARM::LR)
+ .addReg(ARM::LR)
+ .addReg(XorReg)
+ .add(predOps(Pred, PredReg))
+ .add(condCodeOp()));
+
+ NewInsts.push_back(
+ BuildMI(MF, DL, TII->get(ARM::tBX_RET)).add(predOps(Pred, PredReg)));
+
+ // Now insert these new instructions after pop.w
+ insertInstsAfter(MI, NewInsts);
+
+ removeInst(MI);
+
+ return true;
+}
+
+//
+// Method: runOnModule()
+//
+// Description:
+// This method is called when the PassManager wants this pass to transform
+// the specified Module. This method
+//
+// * creates a global variable as the shadow stack,
+//
+// * creates a function that initializes the reserved registers for the
+// shadow stack, and
+//
+// * transforms the Module to utilize the shadow stack for saving/restoring
+// return addresses and/or to nullify a saved return address on returns.
+//
+// Input:
+// M - A reference to the Module to transform.
+//
+// Output:
+// M - The transformed Module.
+//
+// Return value:
+// true - The Module was transformed.
+// false - The Module was not transformed.
+//
+bool ARMEncodeDecode::runOnModule(Module &M) {
+ // if (!EnableEncodeDecode) {
+ // return false;
+ // }
+
+ // random number stored in r9
+ MachineModuleInfo &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+ Twine RNGName = getPassName() + "-" + Twine(EncodeDecodeSeed);
+ RNG = M.createRNG(RNGName.str());
+
+ if (EnableEncodeDecode) {
+ // Create an init function that:
+ // * loads the address of the shadow stack to the shadow stack pointer
+ // register, and
+ // * generates a random stride (either dynamic or static) to the shadow
+ // stack stride register
+ //createInitFunction(M);
+ }
+
+ // Instrument pushes and pops in each function
+ bool changed = false;
+
+ for (Function &F : M) {
+
+ // if (F.getName() == "jzx_Sum" || F.getName() == "jzx_foo" ||
+ // F.getName() == "getAddr" ||
+ // F.getName() == "testThread")
+ // {
+ if (F.getName() != "Reset_Handler" )
+ {
+ MachineFunction *MF = MMI.getMachineFunction(F);
+ if (MF == nullptr) {
+ continue;
+ }
+
+ // Find out all pushes that write LR to the stack and all pops that read a
+ // return address from the stack to LR or PC
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> Pushes;
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> Pops;
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> Blxs;
+ MachineOperand *callRegister = nullptr; // 保存最后一个寄存器操作数的指针
+ Register callRegister1 = ARM::R9; // 对标以下的r0
+ Register callRegister2 = ARM::R9; // 对标以下的r0
+ unsigned SPImm;
+
+ for (MachineBasicBlock &MBB : *MF) {
+ for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end();
+ I != E;) {
+ MachineInstr &MI = *I;
+ MachineBasicBlock::iterator I_ = I; // 记录当前位置
+ int state = 1; // 表示当前要找哪一条指令
+ if (MI.getOpcode() == ARM::tBLXr ||
+ MI.getOpcode() == ARM::tBLXr_noip) {
+ for (MachineOperand &MO : MI.explicit_operands()) {
+ if (MO.isReg()) {
+ callRegister = &MO; // 更新最后一个寄存器操作数的指针
+ }
+ }
+ // Blxs.push_back(std::make_pair(&MI, callRegister));
+ while (1) {
+ if (I == MBB.begin()) {
+ break;
+ } else {
+ I--; // 往上找
+ MachineInstr &MI1 = *I; // 取当前的MI1
+ if (state == 1) { // 先找ldr r2, [sp, #16]
+ int x = 0;
+ switch (MI1.getOpcode()) {
+ case ARM::LDRrs:
+ case ARM::PICLDR:
+ case ARM::LDRBrs:
+ case ARM::t2LDRDi8:
+ case ARM::LDRi12:
+ case ARM::t2LDRi12:
+ case ARM::tLDRi:
+ case ARM::tLDRspi:
+ for (MachineOperand &MO : MI1.explicit_operands()) {
+ if (MO.isReg() && callRegister->getReg() == MO.getReg()) {
+ unsigned Idx = MI1.getOperandNo(&MO);
+ callRegister1 = MI1.getOperand(Idx + 1).getReg();
+ SPImm = MI1.getOperand(Idx + 2).getImm();
+ state = 2; // 接下来可以找str r0, [sp, #16] 2
+ }
+ }
+ break;
+ case ARM::tLDRpci:
+ x = 1;
+ break;
+ default:
+ break;
+ }
+ if (x == 1) {
+ break;
+ }
+ } else if (state == 2) {
+ switch (MI1.getOpcode()) {
+ case ARM::STRrs:
+ case ARM::STRBrs:
+ case ARM::t2STRDi8:
+ case ARM::STRi12:
+ case ARM::t2STRi12:
+ case ARM::tSTRi:
+ case ARM::tSTRspi:
+ for (MachineOperand &MO : MI1.explicit_operands()) {
+ if (MO.isReg() && callRegister1 == MO.getReg()) {
+ unsigned Idx = MI1.getOperandNo(&MO); //
+ if (SPImm == MI1.getOperand(Idx + 1).getImm()) {
+ callRegister1 = MI1.getOperand(Idx - 1).getReg();
+ state = 3; // 接下来可以找ldr r0, [r0, #0]
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } else if (state ==
+ 3) { // 找ldr r0, [r0, #0],一步步迭代到add r0,pc
+ int f = 0;
+ switch (MI1.getOpcode()) {
+ case ARM::tPICADD:
+ case ARM::PICADD:
+ for (MachineOperand &MO : MI1.explicit_operands()) {
+ if (MO.isReg() && callRegister1 == MO.getReg()) {
+ unsigned Idx = MI1.getOperandNo(&MO);
+ Blxs.push_back(std::make_pair(&MI, callRegister));
+ f = 1;
+ break;
+ }
+ }
+ break;
+ case ARM::LDRrs:
+ case ARM::PICLDR:
+ case ARM::LDRBrs:
+ case ARM::t2LDRDi8:
+ case ARM::LDRi12:
+ case ARM::t2LDRi12:
+ case ARM::tLDRi:
+ case ARM::tLDRspi:
+ case ARM::tLDRpci:
+ if (MI1.getOperand(0).isReg() &&
+ callRegister1 == MI1.getOperand(0).getReg() &&
+ callRegister1 != MI1.getOperand(1).getReg()) {
+ state = 1; // 转到状态1
+ callRegister1 = MI1.getOperand(1).getReg();
+ }
+ break;
+ default:
+ break;
+ }
+ if (f == 1) {
+ break;
+ }
+ }
+ }
+ }
+ I = I_;
+ }
+ I++;
+ }
+ }
+
+ for (MachineBasicBlock &MBB : *MF) {
+ for (MachineInstr &MI : MBB) {
+ switch (MI.getOpcode()) {
+ // Frame-setup instructions in function prologue
+ case ARM::t2STR_PRE:
+ case ARM::t2STMDB_UPD:
+ // STR_PRE and STMDB_UPD are considered as PUSH if they write to SP!
+ if (MI.getOperand(0).getReg() != ARM::SP) {
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case ARM::tPUSH:
+ if (MI.getFlag(MachineInstr::FrameSetup)) {
+ for (MachineOperand &MO : MI.explicit_operands()) {
+ if (MO.isReg() && MO.getReg() == ARM::LR) {
+ Pushes.push_back(std::make_pair(&MI, &MO));
+ break;
+ }
+ }
+ }
+ break;
+ // Frame-destroy instructions in function epilogue
+ case ARM::t2LDR_POST:
+ case ARM::t2LDMIA_UPD:
+ case ARM::t2LDMIA_RET:
+ // LDR_POST and LDMIA_(UPD|RET) are considered as POP if they read
+ // from SP!
+ if (MI.getOperand(1).getReg() != ARM::SP) {
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case ARM::tPOP:
+ case ARM::tPOP_RET:
+ if (MI.getFlag(MachineInstr::FrameDestroy)) {
+ // Handle 2 cases:
+ // (1) Pop writing to LR
+ // (2) Pop writing to PC
+ for (MachineOperand &MO : MI.explicit_operands()) {
+ if (MO.isReg()) {
+ if (MO.getReg() == ARM::LR || MO.getReg() == ARM::PC) {
+ Pops.push_back(std::make_pair(&MI, &MO));
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // Instrument each push and pop
+ // if (EnableEncodeDecode) {
+ // Generate a per-function static stride
+ uint32_t Stride = (*RNG)();
+ Stride &= (1ul << (EncodeDecodeNumberLength - 1)) - 1;
+ Stride &= ~0x3ul;
+ // Limit the static stride to be within 8 bits, so that it can fit in
+ // STR_POST and LDR_PRE as an immediate
+ Stride &= 0xfful;
+ // Don't generate an empty stride; either the dynamic stride or the
+ // static stride needs to make sure of it, so just do it on the static to
+ // leave more room for the dynamic
+ if (Stride == 0u) {
+ Stride = 4u;
+ }
+
+ for (auto &MIMO : Pushes) {
+ changed |= EncodeLR(*MIMO.first, *MIMO.second, Stride);
+ }
+
+ for (auto &MIMO : Blxs) {
+ changed |= EncodeCallSite(*MIMO.first, *MIMO.second, Stride);
+ }
+
+ for (auto &MIMO : Pops) {
+ changed |= DecodeLR(*MIMO.first, *MIMO.second, Stride);
+ }
+ }
+ }
+
+ return changed;
+}
+
+ModulePass *llvm::createARMEncodeDecode(void) { return new ARMEncodeDecode(); }
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMEncodeDecode.h b/llvm/lib/Target/ARM/ARMEncodeDecode.h
new file mode 100755
index 00000000000000..3b00de8bb71d24
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMEncodeDecode.h
@@ -0,0 +1,38 @@
+#ifndef ARM_ENCODE_DECODE
+#define ARM_ENCODE_DECODE
+
+#include "ARMRandezvousInstrumentor.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+
+namespace llvm {
+ struct ARMEncodeDecode : public ModulePass, ARMRandezvousInstrumentor {
+ // Pass Identifier
+ static char ID;
+
+ // xor number
+ static constexpr Register storeReg = ARM::R8;
+ static constexpr Register XorReg = ARM::R9;
+ static constexpr StringRef InitFuncName = "__xor_register_init";
+
+ ARMEncodeDecode();
+ virtual StringRef getPassName() const override;
+ void getAnalysisUsage(AnalysisUsage & AU) const override;
+ virtual bool runOnModule(Module & M) override;
+
+ private:
+ std::unique_ptr<RandomNumberGenerator> RNG;
+
+ Function * createInitFunction(Module & M);
+ bool EncodeLR(MachineInstr & MI, MachineOperand & LR,
+ uint32_t Stride);
+ bool EncodeCallSite(MachineInstr & MI, MachineOperand & MO,
+ uint32_t Stride);
+ bool DecodeLR(MachineInstr & MI, MachineOperand & PCLR,
+ uint32_t Stride);
+ };
+
+ ModulePass * createARMEncodeDecode(void);
+}
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp b/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
old mode 100644
new mode 100755
index 58dbfca265ad4a..72e3f5dfe9b2ab
--- a/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
+++ b/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
@@ -85,6 +85,15 @@ ShadowStack("arm-randezvous-shadow-stack",
cl::location(EnableRandezvousShadowStack),
cl::init(false));
+//jzx
+bool EnableEncodeDecode;
+static cl::opt<bool, true>
+EncodeDecode("arm-encode-decode",
+ cl::Hidden,
+ cl::desc("Enable ARM Encode and Decode"),
+ cl::location(EnableEncodeDecode),
+ cl::init(true));
+
bool EnableRandezvousRAN;
static cl::opt<bool, true>
RAN("arm-randezvous-ran",
@@ -137,6 +146,15 @@ ShadowStackSeed("arm-randezvous-shadow-stack-seed",
cl::location(RandezvousShadowStackSeed),
cl::init(0));
+//jzx
+uint64_t EncodeDecodeSeed;
+static cl::opt<uint64_t, true>
+XorSeed("encode-decode-seed",
+ cl::Hidden,
+ cl::desc("Seed for the RNG used in ARM Encode and Decode"),
+ cl::location(EncodeDecodeSeed),
+ cl::init(0));
+
//===----------------------------------------------------------------------===//
// Size options used by Randezvous passes
//===----------------------------------------------------------------------===//
@@ -193,6 +211,15 @@ ShadowStackStrideLength("arm-randezvous-shadow-stack-stride-length",
cl::location(RandezvousShadowStackStrideLength),
cl::init(8));
+//jzx
+unsigned EncodeDecodeNumberLength;
+static cl::opt<unsigned, true>
+XorNumLength("arm-xor-number-length",
+ cl::Hidden,
+ cl::desc("Number of bits for ARM Xor Number"),
+ cl::location(EncodeDecodeNumberLength),
+ cl::init(8));
+
unsigned RandezvousNumGlobalGuardCandidates;
static cl::opt<unsigned, true>
NumGlobalGuardCandidates("arm-randezvous-num-global-guard-candidates",
@@ -207,4 +234,13 @@ RNGAddress("arm-randezvous-rng-addr",
cl::Hidden,
cl::desc("Address of a dynamic RNG"),
cl::location(RandezvousRNGAddress),
+ cl::init(0));
+
+//jzx
+uintptr_t EncodeDecodeRNGAddress;
+static cl::opt<uintptr_t, true>
+XorRNGAddress("arm-encode-and-decode-rng-addr",
+ cl::Hidden,
+ cl::desc("Address of a dynamic RNG"),
+ cl::location(EncodeDecodeRNGAddress),
cl::init(0));
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMRandezvousOptions.h b/llvm/lib/Target/ARM/ARMRandezvousOptions.h
old mode 100644
new mode 100755
index 3757fd09e07f42..7e754cdf9e0cf2
--- a/llvm/lib/Target/ARM/ARMRandezvousOptions.h
+++ b/llvm/lib/Target/ARM/ARMRandezvousOptions.h
@@ -30,6 +30,7 @@ extern bool EnableRandezvousGDLR;
extern bool EnableRandezvousDecoyPointers;
extern bool EnableRandezvousGlobalGuard;
extern bool EnableRandezvousShadowStack;
+extern bool EnableEncodeDecode;//jzx
extern bool EnableRandezvousRAN;
extern bool EnableRandezvousLGPromote;
extern bool EnableRandezvousICallLimiter;
@@ -41,6 +42,7 @@ extern bool EnableRandezvousICallLimiter;
extern uint64_t RandezvousCLRSeed;
extern uint64_t RandezvousGDLRSeed;
extern uint64_t RandezvousShadowStackSeed;
+extern uint64_t EncodeDecodeSeed;//jzx
//===----------------------------------------------------------------------===//
// Size options used by Randezvous passes
@@ -57,7 +59,9 @@ extern size_t RandezvousShadowStackSize;
//===----------------------------------------------------------------------===//
extern unsigned RandezvousShadowStackStrideLength;
+extern unsigned EncodeDecodeNumberLength;
extern unsigned RandezvousNumGlobalGuardCandidates;
+extern uintptr_t EncodeDecodeRNGAddress;//jzx
extern uintptr_t RandezvousRNGAddress;
#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
old mode 100644
new mode 100755
index bfd0d0ec4cecda..1f798dc792952e
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -11,6 +11,8 @@
#include "ARMRandezvousInstrumentor.h"
#include "ARMRandezvousShadowStack.h"
+//jzx
+#include "ARMEncodeDecode.h"
#include "ARMTargetMachine.h"
#include "ARM.h"
#include "ARMMachineFunctionInfo.h"
@@ -58,6 +60,9 @@
#include <optional>
#include <string>
+//jzx
+#include "ARMEncodeDecode.h"
+
using namespace llvm;
static cl::opt<bool>
@@ -618,6 +623,9 @@ void ARMPassConfig::addPreEmitPass2() {
addPass(createARMRandezvousShadowStack());
+ //jzx
+ addPass(createARMEncodeDecode());
+
if (TM->getTargetTriple().isOSWindows()) {
// Identify valid longjmp targets for Windows Control Flow Guard.
diff --git a/llvm/lib/Target/ARM/CMakeLists.txt b/llvm/lib/Target/ARM/CMakeLists.txt
old mode 100644
new mode 100755
index d4dd215b84ab83..df33e216107b52
--- a/llvm/lib/Target/ARM/CMakeLists.txt
+++ b/llvm/lib/Target/ARM/CMakeLists.txt
@@ -19,6 +19,9 @@ tablegen(LLVM ARMGenSystemRegister.inc -gen-searchable-tables)
add_public_tablegen_target(ARMCommonTableGen)
+set(ARM_Shadow_Randomization
+ ARMEncodeDecode.cpp
+)
set(ARMRandezvous_SOURCES
ARMRandezvousInstrumentor.cpp
@@ -77,6 +80,7 @@ add_llvm_target(ARMCodeGen
Thumb2InstrInfo.cpp
Thumb2SizeReduction.cpp
${ARMRandezvous_SOURCES}
+ ${ARM_Shadow_Randomization}
LINK_COMPONENTS
ARMDesc
diff --git a/openmp/tools/analyzer/llvm-openmp-analyzer++ b/openmp/tools/analyzer/llvm-openmp-analyzer++
deleted file mode 120000
index b45062ac867626..00000000000000
--- a/openmp/tools/analyzer/llvm-openmp-analyzer++
+++ /dev/null
@@ -1 +0,0 @@
-llvm-openmp-analyzer
\ No newline at end of file
diff --git a/openmp/tools/analyzer/llvm-openmp-analyzer++ b/openmp/tools/analyzer/llvm-openmp-analyzer++
new file mode 100755
index 00000000000000..259809c2dddb29
--- /dev/null
+++ b/openmp/tools/analyzer/llvm-openmp-analyzer++
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+"""
+A wrapper for Clang specialized for gathering information about OpenMP programs.
+Simple replace calls to clang or clang++ with llvm-openmp-analyzer to run the
+analysis passes.
+"""
+
+import argparse
+import subprocess
+import yaml # PyYaml to save and load analysis information
+import sys
+import io
+
+from analyzer import getKernelUsage
+
+desc = '''A wrapper around clang that runs OpenMP Analysis passes and gathers
+information about OpenMP programs.'''
+
+default_args = ["-fopenmp", "-Rpass=openmp-opt", "-Rpass-missed=openmp-opt", "-Rpass-analysis=openmp-opt"]
+
+def main():
+ compiler = ["clang++"] if sys.argv[0].endswith('++') else ["clang"]
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument('--usage-report-file',
+ metavar='filename',
+ default='usage.yaml',
+ help='Filename used for the OpenMP kernel usage reports in YAML format. "usage.yaml" by default.')
+ parser.add_argument('--no-usage-report',
+ action='store_true',
+ default=False,
+ help='Do not general a usage report for the OpenMP kernels.')
+ args, clang_args = parser.parse_known_args()
+
+ subprocess.run(compiler + default_args + clang_args, check=True)
+ output = subprocess.run(compiler + default_args + clang_args + ["-v"], stderr=subprocess.PIPE)
+ stderr = output.stderr.decode('utf-8')
+
+ if not args.no_usage_report:
+ usage = getKernelUsage(stderr, fname=args.usage_report_file)
+ with io.open(args.usage_report_file, 'w', encoding = 'utf-8') as f:
+ yaml.dump(usage, f)
+
+if __name__ == '__main__':
+ main()
>From d26a1e94b29fd2cb1abe93302adcb1cfdff7d28a Mon Sep 17 00:00:00 2001
From: jiang-zixian <980485445 at qq.com>
Date: Sun, 31 Mar 2024 23:49:41 +0800
Subject: [PATCH 4/8] edit the reademe.md to add commands for compile
---
README.md | 29 +++++++++++++++++++++++++
llvm/lib/Target/ARM/ARMEncodeDecode.cpp | 1 +
2 files changed, 30 insertions(+)
diff --git a/README.md b/README.md
index 1273ba17c2fa0c..631ba44fff154e 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,32 @@
+# srtp小组 可用编译命令
+
+1. 设置交换分区
+```bash
+sudo rm /var/cache/swap/swap0
+sudo swapoff -a
+sudo mkdir -p /var/cache/swap/
+sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=256
+#(这里的count可以小一点,1024完全ok,更小一些不知道够不够)
+sudo chmod 0600 /var/cache/swap/swap0
+sudo mkswap /var/cache/swap/swap0
+sudo swapon /var/cache/swap/swap0
+sudo swapon -s
+```
+
+2. 进入build文件夹编译
+```bash
+cd llvm-project
+sudo mkdir build
+cd build
+```
+3. 编译
+```bash
+sudo cmake -G "Ninja" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;" ../llvm -DCMAKE_BUILD_TYPE=release &&sudo ninja -j3
+```
+ninja命令后的-j后跟的数字可根据自己电脑内核数修改,建议比电脑配置内核数少一个,否则电脑其他进程会很卡
+
+
+
# The LLVM Compiler Infrastructure
This directory and its sub-directories contain the source code for LLVM,
diff --git a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
index ddf73811254f23..b2bd7fc9401600 100755
--- a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
+++ b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
@@ -271,6 +271,7 @@ bool ARMEncodeDecode::EncodeLR(MachineInstr &MI, MachineOperand &LR,
return true;
}
+//
bool ARMEncodeDecode::EncodeCallSite(MachineInstr &MI, MachineOperand &MO,
uint32_t Stride) {
MachineFunction &MF = *MI.getMF();
>From 84b6378ebc1196c159d50be1da847ab64d75c4eb Mon Sep 17 00:00:00 2001
From: jiang-zixian <980485445 at qq.com>
Date: Sun, 31 Mar 2024 23:52:28 +0800
Subject: [PATCH 5/8] add commands for compile
---
README.md | 29 -----------------------------
commands.md | 28 ++++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 29 deletions(-)
create mode 100644 commands.md
diff --git a/README.md b/README.md
index 631ba44fff154e..1273ba17c2fa0c 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,3 @@
-# srtp小组 可用编译命令
-
-1. 设置交换分区
-```bash
-sudo rm /var/cache/swap/swap0
-sudo swapoff -a
-sudo mkdir -p /var/cache/swap/
-sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=256
-#(这里的count可以小一点,1024完全ok,更小一些不知道够不够)
-sudo chmod 0600 /var/cache/swap/swap0
-sudo mkswap /var/cache/swap/swap0
-sudo swapon /var/cache/swap/swap0
-sudo swapon -s
-```
-
-2. 进入build文件夹编译
-```bash
-cd llvm-project
-sudo mkdir build
-cd build
-```
-3. 编译
-```bash
-sudo cmake -G "Ninja" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;" ../llvm -DCMAKE_BUILD_TYPE=release &&sudo ninja -j3
-```
-ninja命令后的-j后跟的数字可根据自己电脑内核数修改,建议比电脑配置内核数少一个,否则电脑其他进程会很卡
-
-
-
# The LLVM Compiler Infrastructure
This directory and its sub-directories contain the source code for LLVM,
diff --git a/commands.md b/commands.md
new file mode 100644
index 00000000000000..b989ad0a15a491
--- /dev/null
+++ b/commands.md
@@ -0,0 +1,28 @@
+# srtp小组 可用编译命令
+
+1. 设置交换分区
+```bash
+sudo rm /var/cache/swap/swap0
+sudo swapoff -a
+sudo mkdir -p /var/cache/swap/
+sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=256
+#(这里的count可以小一点,1024完全ok,更小一些不知道够不够)
+sudo chmod 0600 /var/cache/swap/swap0
+sudo mkswap /var/cache/swap/swap0
+sudo swapon /var/cache/swap/swap0
+sudo swapon -s
+```
+
+2. 进入build文件夹编译
+```bash
+cd llvm-project
+sudo mkdir build
+cd build
+```
+3. 编译
+```bash
+sudo cmake -G "Ninja" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;" ../llvm -DCMAKE_BUILD_TYPE=release &&sudo ninja -j3
+```
+ninja命令后的-j后跟的数字可根据自己电脑内核数修改,建议比电脑配置内核数少一个,否则电脑其他进程会很卡
+
+
>From 486da820ff714d5757c3699472ac209b2fb34fcd Mon Sep 17 00:00:00 2001
From: jiang-zixian <980485445 at qq.com>
Date: Sun, 31 Mar 2024 23:54:17 +0800
Subject: [PATCH 6/8] edit the readme.md
---
README.md | 30 ++++++++++++++++++++++++++++++
commands.md | 28 ----------------------------
2 files changed, 30 insertions(+), 28 deletions(-)
delete mode 100644 commands.md
diff --git a/README.md b/README.md
index 1273ba17c2fa0c..7f052397e868db 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,33 @@
+# srtp小组 可用编译命令
+
+1. 设置交换分区
+```bash
+sudo rm /var/cache/swap/swap0
+sudo swapoff -a
+sudo mkdir -p /var/cache/swap/
+sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=256
+#(这里的count可以小一点,1024完全ok,更小一些不知道够不够)
+sudo chmod 0600 /var/cache/swap/swap0
+sudo mkswap /var/cache/swap/swap0
+sudo swapon /var/cache/swap/swap0
+sudo swapon -s
+```
+
+2. 进入build文件夹编译
+```bash
+cd llvm-project
+sudo mkdir build
+cd build
+```
+3. 编译
+```bash
+sudo cmake -G "Ninja" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;" ../llvm -DCMAKE_BUILD_TYPE=release &&sudo ninja -j3
+```
+ninja命令后的-j后跟的数字可根据自己电脑内核数修改,建议比电脑配置内核数少一个,否则电脑其他进程会很卡
+
+
+
+
# The LLVM Compiler Infrastructure
This directory and its sub-directories contain the source code for LLVM,
diff --git a/commands.md b/commands.md
deleted file mode 100644
index b989ad0a15a491..00000000000000
--- a/commands.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# srtp小组 可用编译命令
-
-1. 设置交换分区
-```bash
-sudo rm /var/cache/swap/swap0
-sudo swapoff -a
-sudo mkdir -p /var/cache/swap/
-sudo dd if=/dev/zero of=/var/cache/swap/swap0 bs=64M count=256
-#(这里的count可以小一点,1024完全ok,更小一些不知道够不够)
-sudo chmod 0600 /var/cache/swap/swap0
-sudo mkswap /var/cache/swap/swap0
-sudo swapon /var/cache/swap/swap0
-sudo swapon -s
-```
-
-2. 进入build文件夹编译
-```bash
-cd llvm-project
-sudo mkdir build
-cd build
-```
-3. 编译
-```bash
-sudo cmake -G "Ninja" -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;" ../llvm -DCMAKE_BUILD_TYPE=release &&sudo ninja -j3
-```
-ninja命令后的-j后跟的数字可根据自己电脑内核数修改,建议比电脑配置内核数少一个,否则电脑其他进程会很卡
-
-
>From 458239ab824bbb01217494ff6874bd92b6507fa2 Mon Sep 17 00:00:00 2001
From: jiang-zixian <980485445 at qq.com>
Date: Sun, 7 Apr 2024 17:00:18 +0800
Subject: [PATCH 7/8] can insert nop before bl
---
llvm/lib/Target/ARM/ARMEncodeDecode.cpp | 101 ++++++++++++++++--
llvm/lib/Target/ARM/ARMEncodeDecode.h | 1 +
.../Target/ARM/MCTargetDesc/ARMAsmBackend.cpp | 64 +++++------
3 files changed, 128 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
index b2bd7fc9401600..e1b1d1e950e162 100755
--- a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
+++ b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
@@ -271,6 +271,30 @@ bool ARMEncodeDecode::EncodeLR(MachineInstr &MI, MachineOperand &LR,
return true;
}
+bool ARMEncodeDecode::insertNop(MachineInstr &MI) {
+ MachineFunction &MF = *MI.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc &DL = MI.getDebugLoc();
+
+ Register PredReg;
+ ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
+
+ // 2. Build the xor instruction
+ // eor.w lr,lr,r9
+
+ std::vector<MachineInstr *> NewInsts;
+ for(int i=0;i<4;i++){
+ NewInsts.push_back(
+ BuildMI(MF,DL,TII->get(ARM::tHINT)).addImm(0).addImm(ARMCC::AL).addReg(0));
+ }
+
+ // 4. Now insert these new instructions into the basic block
+ insertInstsBefore(MI, NewInsts);
+
+ ++NumPrologues;
+ return true;
+}
+
//
bool ARMEncodeDecode::EncodeCallSite(MachineInstr &MI, MachineOperand &MO,
uint32_t Stride) {
@@ -377,6 +401,7 @@ bool ARMEncodeDecode::DecodeLR(MachineInstr &MI, MachineOperand &PCLR,
// true - The Module was transformed.
// false - The Module was not transformed.
//
+
bool ARMEncodeDecode::runOnModule(Module &M) {
// if (!EnableEncodeDecode) {
// return false;
@@ -400,11 +425,6 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
bool changed = false;
for (Function &F : M) {
-
- // if (F.getName() == "jzx_Sum" || F.getName() == "jzx_foo" ||
- // F.getName() == "getAddr" ||
- // F.getName() == "testThread")
- // {
if (F.getName() != "Reset_Handler" )
{
MachineFunction *MF = MMI.getMachineFunction(F);
@@ -417,6 +437,7 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
std::vector<std::pair<MachineInstr *, MachineOperand *>> Pushes;
std::vector<std::pair<MachineInstr *, MachineOperand *>> Pops;
std::vector<std::pair<MachineInstr *, MachineOperand *>> Blxs;
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> Bs;
MachineOperand *callRegister = nullptr; // 保存最后一个寄存器操作数的指针
Register callRegister1 = ARM::R9; // 对标以下的r0
Register callRegister2 = ARM::R9; // 对标以下的r0
@@ -586,7 +607,28 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
}
}
break;
-
+ case ARM::BL:
+ case ARM::BL_pred:
+ case ARM::BMOVPCB_CALL:
+ case ARM::BL_PUSHLR:
+ case ARM::BLXi:
+ case ARM::tBL:
+ case ARM::tBLXi:
+ case ARM::tBL_PUSHLR:
+ case ARM::tBLXr:
+ case ARM::tBLXr_noip:
+ case ARM::tBLXNSr:
+ case ARM::tBLXNS_CALL:
+ case ARM::tBX_CALL:
+ case ARM::tTAILJMPr:
+ case ARM::BLX:
+ case ARM::BLX_noip:
+ case ARM::BLX_pred:
+ case ARM::BLX_pred_noip:
+ case ARM::BX_CALL:
+ case ARM::BMOVPCRX_CALL:
+ Bs.push_back(std::make_pair(&MI, nullptr));
+ break;
default:
break;
}
@@ -613,6 +655,10 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
changed |= EncodeLR(*MIMO.first, *MIMO.second, Stride);
}
+ for (auto &MIMO : Bs) {
+ changed |= insertNop(*MIMO.first);
+ }
+
for (auto &MIMO : Blxs) {
changed |= EncodeCallSite(*MIMO.first, *MIMO.second, Stride);
}
@@ -620,7 +666,50 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
for (auto &MIMO : Pops) {
changed |= DecodeLR(*MIMO.first, *MIMO.second, Stride);
}
+ }else{
+ MachineFunction *MF = MMI.getMachineFunction(F);
+ if (MF == nullptr) {
+ continue;
+ }
+
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> Bs;
+
+ for (MachineBasicBlock &MBB : *MF) {
+ for (MachineInstr &MI : MBB) {
+ switch (MI.getOpcode()) {
+ case ARM::BL:
+ case ARM::BL_pred:
+ case ARM::BMOVPCB_CALL:
+ case ARM::BL_PUSHLR:
+ case ARM::BLXi:
+ case ARM::tBL:
+ case ARM::tBLXi:
+ case ARM::tBL_PUSHLR:
+ case ARM::tBLXr:
+ case ARM::tBLXr_noip:
+ case ARM::tBLXNSr:
+ case ARM::tBLXNS_CALL:
+ case ARM::tBX_CALL:
+ case ARM::tTAILJMPr:
+ case ARM::BLX:
+ case ARM::BLX_noip:
+ case ARM::BLX_pred:
+ case ARM::BLX_pred_noip:
+ case ARM::BX_CALL:
+ case ARM::BMOVPCRX_CALL:
+ Bs.push_back(std::make_pair(&MI, nullptr));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ for (auto &MIMO : Bs) {
+ changed |= insertNop(*MIMO.first);
+ }
}
+
}
return changed;
diff --git a/llvm/lib/Target/ARM/ARMEncodeDecode.h b/llvm/lib/Target/ARM/ARMEncodeDecode.h
index 3b00de8bb71d24..ae2812250b6b8d 100755
--- a/llvm/lib/Target/ARM/ARMEncodeDecode.h
+++ b/llvm/lib/Target/ARM/ARMEncodeDecode.h
@@ -26,6 +26,7 @@ namespace llvm {
Function * createInitFunction(Module & M);
bool EncodeLR(MachineInstr & MI, MachineOperand & LR,
uint32_t Stride);
+ bool insertNop(MachineInstr &MI);
bool EncodeCallSite(MachineInstr & MI, MachineOperand & MO,
uint32_t Stride);
bool DecodeLR(MachineInstr & MI, MachineOperand & PCLR,
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
index 1a2cab20ee07c3..83384f8bc281ae 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
@@ -246,8 +246,8 @@ bool ARMAsmBackend::mayNeedRelaxation(const MCInst &Inst,
static const char *checkPCRelOffset(uint64_t Value, int64_t Min, int64_t Max) {
int64_t Offset = int64_t(Value) - 4;
- if (Offset < Min || Offset > Max)
- return "out of range pc-relative fixup value";
+ // if (Offset < Min || Offset > Max)
+ // return "out of range pc-relative fixup value";
return nullptr;
}
@@ -262,8 +262,8 @@ const char *ARMAsmBackend::reasonForFixupRelaxation(const MCFixup &Fixup,
//
// Relax if the value is too big for a (signed) i8.
int64_t Offset = int64_t(Value) - 4;
- if (Offset > 2046 || Offset < -2048)
- return "out of range pc-relative fixup value";
+ // if (Offset > 2046 || Offset < -2048)
+ // return "out of range pc-relative fixup value";
break;
}
case ARM::fixup_arm_thumb_bcc: {
@@ -274,8 +274,8 @@ const char *ARMAsmBackend::reasonForFixupRelaxation(const MCFixup &Fixup,
//
// Relax if the value is too big for a (signed) i8.
int64_t Offset = int64_t(Value) - 4;
- if (Offset > 254 || Offset < -256)
- return "out of range pc-relative fixup value";
+ // if (Offset > 254 || Offset < -256)
+ // return "out of range pc-relative fixup value";
break;
}
case ARM::fixup_thumb_adr_pcrel_10:
@@ -285,8 +285,8 @@ const char *ARMAsmBackend::reasonForFixupRelaxation(const MCFixup &Fixup,
int64_t Offset = int64_t(Value) - 4;
if (Offset & 3)
return "misaligned pc-relative fixup value";
- else if (Offset > 1020 || Offset < 0)
- return "out of range pc-relative fixup value";
+ // else if (Offset > 1020 || Offset < 0)
+ // return "out of range pc-relative fixup value";
break;
}
case ARM::fixup_arm_thumb_cb: {
@@ -525,10 +525,10 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
Value = -Value;
isAdd = false;
}
- if (Value >= 4096) {
- Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
- return 0;
- }
+ // if (Value >= 4096) {
+ // Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+ // return 0;
+ // }
Value |= isAdd << 23;
// Same addressing mode as fixup_arm_pcrel_10,
@@ -546,10 +546,10 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
Value = -Value;
opc = 2; // 0b0010
}
- if (ARM_AM::getSOImmVal(Value) == -1) {
- Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
- return 0;
- }
+ // if (ARM_AM::getSOImmVal(Value) == -1) {
+ // Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+ // return 0;
+ // }
// Encode the immediate and shift the opcode into place.
return ARM_AM::getSOImmVal(Value) | (opc << 21);
}
@@ -714,10 +714,10 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
// CB instructions can only branch to offsets in [4, 126] in multiples of 2
// so ensure that the raw value LSB is zero and it lies in [2, 130].
// An offset of 2 will be relaxed to a NOP.
- if ((int64_t)Value < 2 || Value > 0x82 || Value & 1) {
- Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
- return 0;
- }
+ // if ((int64_t)Value < 2 || Value > 0x82 || Value & 1) {
+ // Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+ // return 0;
+ // }
// Offset by 4 and don't encode the lower bit, which is always 0.
// FIXME: diagnose if no Thumb2
uint32_t Binary = (Value - 4) >> 1;
@@ -755,10 +755,10 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
isAdd = false;
}
// The value has the low 4 bits encoded in [3:0] and the high 4 in [11:8].
- if (Value >= 256) {
- Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
- return 0;
- }
+ // if (Value >= 256) {
+ // Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+ // return 0;
+ // }
Value = (Value & 0xf) | ((Value & 0xf0) << 4);
return Value | (isAdd << 23);
}
@@ -776,10 +776,10 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
}
// These values don't encode the low two bits since they're always zero.
Value >>= 2;
- if (Value >= 256) {
- Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
- return 0;
- }
+ // if (Value >= 256) {
+ // Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+ // return 0;
+ // }
Value |= isAdd << 23;
// Same addressing mode as fixup_arm_pcrel_10, but with 16-bit halfwords
@@ -807,10 +807,10 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
return 0;
}
Value >>= 1;
- if (Value >= 256) {
- Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
- return 0;
- }
+ // if (Value >= 256) {
+ // Ctx.reportError(Fixup.getLoc(), "out of range pc-relative fixup value");
+ // return 0;
+ // }
Value |= isAdd << 23;
// Same addressing mode as fixup_arm_pcrel_9, but with 16-bit halfwords
>From 473f894c098e764d3abf523b1b7427cac312de88 Mon Sep 17 00:00:00 2001
From: jiang-zixian <980485445 at qq.com>
Date: Thu, 11 Apr 2024 15:13:16 +0800
Subject: [PATCH 8/8] can insert nop before bl and update blx
---
llvm/lib/Target/ARM/ARMEncodeDecode.cpp | 69 -------
llvm/lib/Target/ARM/ARMRandezvousOptions.cpp | 8 +
llvm/lib/Target/ARM/ARMRandezvousOptions.h | 1 +
llvm/lib/Target/ARM/ARMTargetMachine.cpp | 5 +-
llvm/lib/Target/ARM/ARMTrampoline.cpp | 196 +++++++++++++++++++
llvm/lib/Target/ARM/ARMTrampoline.h | 26 +++
llvm/lib/Target/ARM/CMakeLists.txt | 1 +
7 files changed, 234 insertions(+), 72 deletions(-)
create mode 100644 llvm/lib/Target/ARM/ARMTrampoline.cpp
create mode 100644 llvm/lib/Target/ARM/ARMTrampoline.h
diff --git a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
index e1b1d1e950e162..9c8bd085273515 100755
--- a/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
+++ b/llvm/lib/Target/ARM/ARMEncodeDecode.cpp
@@ -437,7 +437,6 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
std::vector<std::pair<MachineInstr *, MachineOperand *>> Pushes;
std::vector<std::pair<MachineInstr *, MachineOperand *>> Pops;
std::vector<std::pair<MachineInstr *, MachineOperand *>> Blxs;
- std::vector<std::pair<MachineInstr *, MachineOperand *>> Bs;
MachineOperand *callRegister = nullptr; // 保存最后一个寄存器操作数的指针
Register callRegister1 = ARM::R9; // 对标以下的r0
Register callRegister2 = ARM::R9; // 对标以下的r0
@@ -607,28 +606,6 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
}
}
break;
- case ARM::BL:
- case ARM::BL_pred:
- case ARM::BMOVPCB_CALL:
- case ARM::BL_PUSHLR:
- case ARM::BLXi:
- case ARM::tBL:
- case ARM::tBLXi:
- case ARM::tBL_PUSHLR:
- case ARM::tBLXr:
- case ARM::tBLXr_noip:
- case ARM::tBLXNSr:
- case ARM::tBLXNS_CALL:
- case ARM::tBX_CALL:
- case ARM::tTAILJMPr:
- case ARM::BLX:
- case ARM::BLX_noip:
- case ARM::BLX_pred:
- case ARM::BLX_pred_noip:
- case ARM::BX_CALL:
- case ARM::BMOVPCRX_CALL:
- Bs.push_back(std::make_pair(&MI, nullptr));
- break;
default:
break;
}
@@ -655,10 +632,6 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
changed |= EncodeLR(*MIMO.first, *MIMO.second, Stride);
}
- for (auto &MIMO : Bs) {
- changed |= insertNop(*MIMO.first);
- }
-
for (auto &MIMO : Blxs) {
changed |= EncodeCallSite(*MIMO.first, *MIMO.second, Stride);
}
@@ -666,48 +639,6 @@ bool ARMEncodeDecode::runOnModule(Module &M) {
for (auto &MIMO : Pops) {
changed |= DecodeLR(*MIMO.first, *MIMO.second, Stride);
}
- }else{
- MachineFunction *MF = MMI.getMachineFunction(F);
- if (MF == nullptr) {
- continue;
- }
-
- std::vector<std::pair<MachineInstr *, MachineOperand *>> Bs;
-
- for (MachineBasicBlock &MBB : *MF) {
- for (MachineInstr &MI : MBB) {
- switch (MI.getOpcode()) {
- case ARM::BL:
- case ARM::BL_pred:
- case ARM::BMOVPCB_CALL:
- case ARM::BL_PUSHLR:
- case ARM::BLXi:
- case ARM::tBL:
- case ARM::tBLXi:
- case ARM::tBL_PUSHLR:
- case ARM::tBLXr:
- case ARM::tBLXr_noip:
- case ARM::tBLXNSr:
- case ARM::tBLXNS_CALL:
- case ARM::tBX_CALL:
- case ARM::tTAILJMPr:
- case ARM::BLX:
- case ARM::BLX_noip:
- case ARM::BLX_pred:
- case ARM::BLX_pred_noip:
- case ARM::BX_CALL:
- case ARM::BMOVPCRX_CALL:
- Bs.push_back(std::make_pair(&MI, nullptr));
- break;
- default:
- break;
- }
- }
- }
-
- for (auto &MIMO : Bs) {
- changed |= insertNop(*MIMO.first);
- }
}
}
diff --git a/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp b/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
index 72e3f5dfe9b2ab..fb6956da20fc66 100755
--- a/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
+++ b/llvm/lib/Target/ARM/ARMRandezvousOptions.cpp
@@ -92,6 +92,14 @@ EncodeDecode("arm-encode-decode",
cl::Hidden,
cl::desc("Enable ARM Encode and Decode"),
cl::location(EnableEncodeDecode),
+ cl::init(false));
+
+bool EnableTrampoline;//jzx
+static cl::opt<bool, true>
+Trampoline("arm-trampoline",
+ cl::Hidden,
+ cl::desc("Enable ARM trampoline"),
+ cl::location(EnableTrampoline),
cl::init(true));
bool EnableRandezvousRAN;
diff --git a/llvm/lib/Target/ARM/ARMRandezvousOptions.h b/llvm/lib/Target/ARM/ARMRandezvousOptions.h
index 7e754cdf9e0cf2..418391f5c18951 100755
--- a/llvm/lib/Target/ARM/ARMRandezvousOptions.h
+++ b/llvm/lib/Target/ARM/ARMRandezvousOptions.h
@@ -31,6 +31,7 @@ extern bool EnableRandezvousDecoyPointers;
extern bool EnableRandezvousGlobalGuard;
extern bool EnableRandezvousShadowStack;
extern bool EnableEncodeDecode;//jzx
+extern bool EnableTrampoline;//jzx
extern bool EnableRandezvousRAN;
extern bool EnableRandezvousLGPromote;
extern bool EnableRandezvousICallLimiter;
diff --git a/llvm/lib/Target/ARM/ARMTargetMachine.cpp b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
index 1f798dc792952e..79c08e926c8701 100755
--- a/llvm/lib/Target/ARM/ARMTargetMachine.cpp
+++ b/llvm/lib/Target/ARM/ARMTargetMachine.cpp
@@ -11,8 +11,6 @@
#include "ARMRandezvousInstrumentor.h"
#include "ARMRandezvousShadowStack.h"
-//jzx
-#include "ARMEncodeDecode.h"
#include "ARMTargetMachine.h"
#include "ARM.h"
#include "ARMMachineFunctionInfo.h"
@@ -62,6 +60,7 @@
//jzx
#include "ARMEncodeDecode.h"
+#include "ARMTrampoline.h"
using namespace llvm;
@@ -625,7 +624,7 @@ void ARMPassConfig::addPreEmitPass2() {
//jzx
addPass(createARMEncodeDecode());
-
+ addPass(createARMTrampoline());
if (TM->getTargetTriple().isOSWindows()) {
// Identify valid longjmp targets for Windows Control Flow Guard.
diff --git a/llvm/lib/Target/ARM/ARMTrampoline.cpp b/llvm/lib/Target/ARM/ARMTrampoline.cpp
new file mode 100644
index 00000000000000..be28669ddf0e17
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMTrampoline.cpp
@@ -0,0 +1,196 @@
+//===- ARMRandezvousShadowStack.cpp - ARM Randezvous Shadow Stack ---------===//
+//
+// Copyright (c) 2021-2022, University of Rochester
+//
+// Part of the Randezvous Project, under the Apache License v2.0 with
+// LLVM Exceptions. See LICENSE.txt in the llvm directory for license
+// information.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of a pass that instruments ARM machine
+// code to save/load the return address to/from a randomized compact shadow
+// stack.
+//
+//===----------------------------------------------------------------------===//
+
+#define DEBUG_TYPE "arm_shadow_randomization"
+
+#include "ARMTrampoline.h"
+#include "ARMRandezvousCLR.h"
+#include "ARMRandezvousOptions.h"
+#include "MCTargetDesc/ARMAddressingModes.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+using namespace llvm;
+
+STATISTIC(NumPrologues, "Number of prologues transformed to use shadow stack");
+STATISTIC(NumEpilogues, "Number of epilogues transformed to use shadow stack");
+STATISTIC(NumNullified, "Number of return addresses nullified");
+
+char ARMTrampoline::ID = 0;
+
+ARMTrampoline::ARMTrampoline() : ModulePass(ID) {}
+
+StringRef ARMTrampoline::getPassName() const {
+ return "ARM Trampoline Pass";
+}
+
+void ARMTrampoline::getAnalysisUsage(AnalysisUsage &AU) const {
+ // We need this to access MachineFunctions
+ AU.addRequired<MachineModuleInfoWrapperPass>();
+
+ AU.setPreservesCFG();
+ ModulePass::getAnalysisUsage(AU);
+}
+
+bool ARMTrampoline::insertNop(MachineInstr &MI) {
+ MachineFunction &MF = *MI.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc &DL = MI.getDebugLoc();
+
+ std::vector<MachineInstr *> NewInsts;
+ for(int i=0;i<4;i++){
+ NewInsts.push_back(
+ BuildMI(MF,DL,TII->get(ARM::tHINT)).addImm(0).addImm(ARMCC::AL).addReg(0));//Nop
+ }
+
+ // 4. Now insert these new instructions into the basic block
+ insertInstsBefore(MI, NewInsts);
+
+ ++NumPrologues;
+ return true;
+}
+
+bool ARMTrampoline::BlxTrampoline(MachineInstr &MI, MachineOperand &MO) {
+ // before update:
+ // blx ri
+
+ // after update:
+ // mov r8,ri
+ // blx r12
+
+ MachineFunction &MF = *MI.getMF();
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const DebugLoc &DL = MI.getDebugLoc();
+
+ std::vector<MachineInstr *> NewInsts;
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tMOVr),ARM::R8)
+ .addReg(MI.getOperand(2).getReg())
+ .add(predOps(ARMCC::AL)));
+
+ NewInsts.push_back(BuildMI(MF, DL, TII->get(ARM::tBLXr))
+ .add(predOps(ARMCC::AL))
+ .addReg(ARM::R11));
+ insertInstsBefore(MI, NewInsts);
+
+ removeInst(MI);
+
+ return true;
+}
+
+
+//
+// Method: runOnModule()
+//
+// Description:
+// This method is called when the PassManager wants this pass to transform
+// the specified Module. This method
+//
+// * creates a global variable as the shadow stack,
+//
+// * creates a function that initializes the reserved registers for the
+// shadow stack, and
+//
+// * transforms the Module to utilize the shadow stack for saving/restoring
+// return addresses and/or to nullify a saved return address on returns.
+//
+// Input:
+// M - A reference to the Module to transform.
+//
+// Output:
+// M - The transformed Module.
+//
+// Return value:
+// true - The Module was transformed.
+// false - The Module was not transformed.
+//
+
+bool ARMTrampoline::runOnModule(Module &M) {
+ if (!EnableTrampoline) {
+ return false;
+ }
+
+ MachineModuleInfo &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+
+ // Instrument pushes and pops in each function
+ bool changed = false;
+
+ for (Function &F : M) {
+ MachineFunction *MF = MMI.getMachineFunction(F);
+ if (MF == nullptr) {
+ continue;
+ }
+
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> BLs;
+ std::vector<std::pair<MachineInstr *, MachineOperand *>> BLXs;
+
+ for (MachineBasicBlock &MBB : *MF) {
+ for (MachineInstr &MI : MBB) {
+ switch (MI.getOpcode()) {
+ case ARM::BL:
+ case ARM::BL_pred:
+ case ARM::BMOVPCB_CALL:
+ case ARM::BL_PUSHLR:
+ case ARM::tBL:
+ case ARM::tBL_PUSHLR:
+ case ARM::tTAILJMPr:
+ case ARM::BMOVPCRX_CALL:
+ BLs.push_back(std::make_pair(&MI, nullptr));
+ break;
+ case ARM::BLX:
+ case ARM::BLX_noip:
+ case ARM::BLX_pred:
+ case ARM::BLX_pred_noip:
+ case ARM::BX_CALL:
+ case ARM::tBLXr:
+ case ARM::tBLXr_noip:
+ case ARM::tBLXNSr:
+ case ARM::tBLXNS_CALL:
+ case ARM::tBX_CALL:
+ case ARM::tBLXi:
+ case ARM::BLXi:
+ for (MachineOperand &MO : MI.explicit_operands()) {
+ if (MO.isReg()) {
+ if (MO.getReg() != ARM::LR) {
+ BLXs.push_back(std::make_pair(&MI, &MO));
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ for (auto &MIMO : BLs) {
+ changed |= insertNop(*MIMO.first);
+ }
+
+ for (auto &MIMO : BLXs) {
+ changed |= BlxTrampoline(*MIMO.first,*MIMO.second);
+ }
+
+ }
+
+ return changed;
+}
+
+ModulePass *llvm::createARMTrampoline(void) { return new ARMTrampoline(); }
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/ARMTrampoline.h b/llvm/lib/Target/ARM/ARMTrampoline.h
new file mode 100644
index 00000000000000..afe812858d639d
--- /dev/null
+++ b/llvm/lib/Target/ARM/ARMTrampoline.h
@@ -0,0 +1,26 @@
+#ifndef ARM_TRAMPOLINE
+#define ARM_TRAMPOLINE
+
+#include "ARMRandezvousInstrumentor.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+
+namespace llvm {
+ struct ARMTrampoline : public ModulePass, ARMRandezvousInstrumentor {
+ // Pass Identifier
+ static char ID;
+
+ ARMTrampoline();
+ virtual StringRef getPassName() const override;
+ void getAnalysisUsage(AnalysisUsage & AU) const override;
+ virtual bool runOnModule(Module & M) override;
+
+ private:
+ bool insertNop(MachineInstr &MI);
+ bool BlxTrampoline(MachineInstr &MI, MachineOperand &MO);
+ };
+
+ ModulePass * createARMTrampoline(void);
+}
+
+#endif
\ No newline at end of file
diff --git a/llvm/lib/Target/ARM/CMakeLists.txt b/llvm/lib/Target/ARM/CMakeLists.txt
index df33e216107b52..3115f8b44fc5df 100755
--- a/llvm/lib/Target/ARM/CMakeLists.txt
+++ b/llvm/lib/Target/ARM/CMakeLists.txt
@@ -21,6 +21,7 @@ add_public_tablegen_target(ARMCommonTableGen)
set(ARM_Shadow_Randomization
ARMEncodeDecode.cpp
+ ARMTrampoline.cpp
)
set(ARMRandezvous_SOURCES
More information about the libcxx-commits
mailing list