blob: e4650a1d547f5ae3cd8e95dd26e067d730ee9e15 [file] [log] [blame]
/*
* Copyright (C) 2015-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "testb3.h"
#include <wtf/UniqueArray.h>
#if ENABLE(B3_JIT)
template<typename T>
void testAtomicWeakCAS()
{
constexpr Type type = NativeTraits<T>::type;
constexpr Width width = NativeTraits<T>::width;
auto checkMyDisassembly = [&] (Compilation& compilation, bool fenced) {
if (isX86()) {
checkUsesInstruction(compilation, "lock");
checkUsesInstruction(compilation, "cmpxchg");
} else {
if (fenced) {
checkUsesInstruction(compilation, "ldax");
checkUsesInstruction(compilation, "stlx");
} else {
checkUsesInstruction(compilation, "ldx");
checkUsesInstruction(compilation, "stx");
}
}
};
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* reloop = proc.addBlock();
BasicBlock* done = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(proc, Jump, Origin());
root->setSuccessors(reloop);
reloop->appendNew<Value>(
proc, Branch, Origin(),
reloop->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
reloop->appendIntConstant(proc, Origin(), type, 42),
reloop->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr));
reloop->setSuccessors(done, reloop);
done->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* reloop = proc.addBlock();
BasicBlock* done = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(proc, Jump, Origin());
root->setSuccessors(reloop);
reloop->appendNew<Value>(
proc, Branch, Origin(),
reloop->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
reloop->appendIntConstant(proc, Origin(), type, 42),
reloop->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr, 0, HeapRange(42), HeapRange()));
reloop->setSuccessors(done, reloop);
done->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, false);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* succ = proc.addBlock();
BasicBlock* fail = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr));
root->setSuccessors(succ, fail);
succ->appendNew<MemoryValue>(
proc, storeOpcode(GP, width), Origin(),
succ->appendIntConstant(proc, Origin(), type, 100),
ptr);
succ->appendNew<Value>(proc, Return, Origin());
fail->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
while (value[0] == 42)
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(100));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* succ = proc.addBlock();
BasicBlock* fail = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr),
root->appendIntConstant(proc, Origin(), Int32, 0)));
root->setSuccessors(fail, succ);
succ->appendNew<MemoryValue>(
proc, storeOpcode(GP, width), Origin(),
succ->appendIntConstant(proc, Origin(), type, 100),
ptr);
succ->appendNew<Value>(proc, Return, Origin());
fail->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
while (value[0] == 42)
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(100));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
while (!invoke<bool>(*code, value)) { }
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK(!invoke<bool>(*code, value));
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
root->appendNew<Const32Value>(proc, Origin(), 0)));
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
while (invoke<bool>(*code, value)) { }
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK(invoke<bool>(*code, value));
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicWeakCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
42));
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
while (!invoke<bool>(*code, bitwise_cast<intptr_t>(value) - 42)) { }
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK(!invoke<bool>(*code, bitwise_cast<intptr_t>(value) - 42));
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
}
template<typename T>
void testAtomicStrongCAS()
{
constexpr Type type = NativeTraits<T>::type;
constexpr Width width = NativeTraits<T>::width;
auto checkMyDisassembly = [&] (Compilation& compilation, bool fenced) {
if (isX86()) {
checkUsesInstruction(compilation, "lock");
checkUsesInstruction(compilation, "cmpxchg");
} else {
if (fenced) {
checkUsesInstruction(compilation, "ldax");
checkUsesInstruction(compilation, "stlx");
} else {
checkUsesInstruction(compilation, "ldx");
checkUsesInstruction(compilation, "stx");
}
}
};
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* succ = proc.addBlock();
BasicBlock* fail = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr),
root->appendIntConstant(proc, Origin(), type, 42)));
root->setSuccessors(succ, fail);
succ->appendNew<MemoryValue>(
proc, storeOpcode(GP, width), Origin(),
succ->appendIntConstant(proc, Origin(), type, 100),
ptr);
succ->appendNew<Value>(proc, Return, Origin());
fail->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(100));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* succ = proc.addBlock();
BasicBlock* fail = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr, 0, HeapRange(42), HeapRange()),
root->appendIntConstant(proc, Origin(), type, 42)));
root->setSuccessors(succ, fail);
succ->appendNew<MemoryValue>(
proc, storeOpcode(GP, width), Origin(),
succ->appendIntConstant(proc, Origin(), type, 100),
ptr);
succ->appendNew<Value>(proc, Return, Origin());
fail->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(100));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, false);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* succ = proc.addBlock();
BasicBlock* fail = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, NotEqual, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr),
root->appendIntConstant(proc, Origin(), type, 42)));
root->setSuccessors(fail, succ);
succ->appendNew<MemoryValue>(
proc, storeOpcode(GP, width), Origin(),
succ->appendIntConstant(proc, Origin(), type, 100),
ptr);
succ->appendNew<Value>(proc, Return, Origin());
fail->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(100));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
invoke<void>(*code, value);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), 42);
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), static_cast<typename NativeTraits<T>::CanonicalType>(static_cast<T>(300)));
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(-1);
CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), static_cast<typename NativeTraits<T>::CanonicalType>(static_cast<T>(-1)));
CHECK_EQ(value[0], static_cast<T>(-1));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
// Test for https://bugs.webkit.org/show_bug.cgi?id=169867.
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<Value>(
proc, BitXor, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
root->appendIntConstant(proc, Origin(), type, 1)));
typename NativeTraits<T>::CanonicalType one = 1;
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), 42 ^ one);
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), static_cast<typename NativeTraits<T>::CanonicalType>(static_cast<T>(300)) ^ one);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(-1);
CHECK_EQ(invoke<typename NativeTraits<T>::CanonicalType>(*code, value), static_cast<typename NativeTraits<T>::CanonicalType>(static_cast<T>(-1)) ^ one);
CHECK_EQ(value[0], static_cast<T>(-1));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
root->appendIntConstant(proc, Origin(), type, 42)));
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
CHECK(invoke<bool>(*code, value));
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK(!invoke<bool>(*code, value));
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<Value>(
proc, NotEqual, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
root->appendIntConstant(proc, Origin(), type, 42)),
root->appendNew<Const32Value>(proc, Origin(), 0)));
auto code = compileProc(proc);
T value[2];
value[0] = 42;
value[1] = 13;
CHECK(invoke<bool>(*code, value));
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
value[0] = static_cast<T>(300);
CHECK(!invoke<bool>(*code, &value));
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
checkMyDisassembly(*code, true);
}
}
template<typename T>
void testAtomicXchg(B3::Opcode opcode)
{
constexpr Type type = NativeTraits<T>::type;
constexpr Width width = NativeTraits<T>::width;
auto doTheMath = [&] (T& memory, T operand) -> T {
T oldValue = memory;
switch (opcode) {
case AtomicXchgAdd:
memory += operand;
break;
case AtomicXchgAnd:
memory &= operand;
break;
case AtomicXchgOr:
memory |= operand;
break;
case AtomicXchgSub:
memory -= operand;
break;
case AtomicXchgXor:
memory ^= operand;
break;
case AtomicXchg:
memory = operand;
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
return oldValue;
};
auto oldValue = [&] (T memory, T operand) -> T {
return doTheMath(memory, operand);
};
auto newValue = [&] (T memory, T operand) -> T {
doTheMath(memory, operand);
return memory;
};
auto checkMyDisassembly = [&] (Compilation& compilation, bool fenced) {
if (isX86())
checkUsesInstruction(compilation, "lock");
else {
if (fenced) {
checkUsesInstruction(compilation, "ldax");
checkUsesInstruction(compilation, "stlx");
} else {
checkUsesInstruction(compilation, "ldx");
checkUsesInstruction(compilation, "stx");
}
}
};
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<AtomicValue>(
proc, opcode, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 1),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
auto code = compileProc(proc);
T value[2];
value[0] = 5;
value[1] = 100;
CHECK_EQ(invoke<T>(*code, value), oldValue(5, 1));
CHECK_EQ(value[0], newValue(5, 1));
CHECK_EQ(value[1], 100);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<AtomicValue>(
proc, opcode, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
auto code = compileProc(proc);
T value[2];
value[0] = 5;
value[1] = 100;
CHECK_EQ(invoke<T>(*code, value), oldValue(5, 42));
CHECK_EQ(value[0], newValue(5, 42));
CHECK_EQ(value[1], 100);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<AtomicValue>(
proc, opcode, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
root->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 5;
value[1] = 100;
invoke<T>(*code, value);
CHECK_EQ(value[0], newValue(5, 42));
CHECK_EQ(value[1], 100);
checkMyDisassembly(*code, true);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<AtomicValue>(
proc, opcode, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 42),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
0, HeapRange(42), HeapRange());
root->appendNew<Value>(proc, Return, Origin());
auto code = compileProc(proc);
T value[2];
value[0] = 5;
value[1] = 100;
invoke<T>(*code, value);
CHECK_EQ(value[0], newValue(5, 42));
CHECK_EQ(value[1], 100);
checkMyDisassembly(*code, false);
}
}
void addAtomicTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
{
RUN(testAtomicWeakCAS<int8_t>());
RUN(testAtomicWeakCAS<int16_t>());
RUN(testAtomicWeakCAS<int32_t>());
RUN(testAtomicWeakCAS<int64_t>());
RUN(testAtomicStrongCAS<int8_t>());
RUN(testAtomicStrongCAS<int16_t>());
RUN(testAtomicStrongCAS<int32_t>());
RUN(testAtomicStrongCAS<int64_t>());
RUN(testAtomicXchg<int8_t>(AtomicXchgAdd));
RUN(testAtomicXchg<int16_t>(AtomicXchgAdd));
RUN(testAtomicXchg<int32_t>(AtomicXchgAdd));
RUN(testAtomicXchg<int64_t>(AtomicXchgAdd));
RUN(testAtomicXchg<int8_t>(AtomicXchgAnd));
RUN(testAtomicXchg<int16_t>(AtomicXchgAnd));
RUN(testAtomicXchg<int32_t>(AtomicXchgAnd));
RUN(testAtomicXchg<int64_t>(AtomicXchgAnd));
RUN(testAtomicXchg<int8_t>(AtomicXchgOr));
RUN(testAtomicXchg<int16_t>(AtomicXchgOr));
RUN(testAtomicXchg<int32_t>(AtomicXchgOr));
RUN(testAtomicXchg<int64_t>(AtomicXchgOr));
RUN(testAtomicXchg<int8_t>(AtomicXchgSub));
RUN(testAtomicXchg<int16_t>(AtomicXchgSub));
RUN(testAtomicXchg<int32_t>(AtomicXchgSub));
RUN(testAtomicXchg<int64_t>(AtomicXchgSub));
RUN(testAtomicXchg<int8_t>(AtomicXchgXor));
RUN(testAtomicXchg<int16_t>(AtomicXchgXor));
RUN(testAtomicXchg<int32_t>(AtomicXchgXor));
RUN(testAtomicXchg<int64_t>(AtomicXchgXor));
RUN(testAtomicXchg<int8_t>(AtomicXchg));
RUN(testAtomicXchg<int16_t>(AtomicXchg));
RUN(testAtomicXchg<int32_t>(AtomicXchg));
RUN(testAtomicXchg<int64_t>(AtomicXchg));
}
template<typename CType, typename InputType>
void testLoad(B3::Type type, B3::Opcode opcode, InputType value)
{
// Simple load from an absolute address.
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(
proc, opcode, type, Origin(),
root->appendNew<ConstPtrValue>(proc, Origin(), &value)));
CHECK(isIdentical(compileAndRun<CType>(proc), modelLoad<CType>(value)));
}
// Simple load from an address in a register.
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(
proc, opcode, type, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
CHECK(isIdentical(compileAndRun<CType>(proc, &value), modelLoad<CType>(value)));
}
// Simple load from an address in a register, at an offset.
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(
proc, opcode, type, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
static_cast<int32_t>(sizeof(InputType))));
CHECK(isIdentical(compileAndRun<CType>(proc, &value - 1), modelLoad<CType>(value)));
}
// Load from a simple base-index with various scales.
for (unsigned logScale = 0; logScale <= 3; ++logScale) {
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(
proc, opcode, type, Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Value>(
proc, Shl, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
root->appendNew<Const32Value>(proc, Origin(), logScale)))));
CHECK(isIdentical(compileAndRun<CType>(proc, &value - 2, (sizeof(InputType) * 2) >> logScale), modelLoad<CType>(value)));
}
// Load from a simple base-index with various scales, but commuted.
for (unsigned logScale = 0; logScale <= 3; ++logScale) {
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(
proc, opcode, type, Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<Value>(
proc, Shl, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
root->appendNew<Const32Value>(proc, Origin(), logScale)),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))));
CHECK(isIdentical(compileAndRun<CType>(proc, &value - 2, (sizeof(InputType) * 2) >> logScale), modelLoad<CType>(value)));
}
}
template<typename T>
void testLoad(B3::Opcode opcode, int32_t value)
{
return testLoad<T>(B3::Int32, opcode, value);
}
template<typename T>
void testLoad(B3::Type type, T value)
{
return testLoad<T>(type, Load, value);
}
void addLoadTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
{
RUN(testLoad(Int32, 60));
RUN(testLoad(Int32, -60));
RUN(testLoad(Int32, 1000));
RUN(testLoad(Int32, -1000));
RUN(testLoad(Int32, 1000000));
RUN(testLoad(Int32, -1000000));
RUN(testLoad(Int32, 1000000000));
RUN(testLoad(Int32, -1000000000));
RUN_BINARY(testLoad, { MAKE_OPERAND(Int64) }, int64Operands());
RUN_BINARY(testLoad, { MAKE_OPERAND(Float) }, floatingPointOperands<float>());
RUN_BINARY(testLoad, { MAKE_OPERAND(Double) }, floatingPointOperands<double>());
RUN(testLoad<int8_t>(Load8S, 60));
RUN(testLoad<int8_t>(Load8S, -60));
RUN(testLoad<int8_t>(Load8S, 1000));
RUN(testLoad<int8_t>(Load8S, -1000));
RUN(testLoad<int8_t>(Load8S, 1000000));
RUN(testLoad<int8_t>(Load8S, -1000000));
RUN(testLoad<int8_t>(Load8S, 1000000000));
RUN(testLoad<int8_t>(Load8S, -1000000000));
RUN(testLoad<uint8_t>(Load8Z, 60));
RUN(testLoad<uint8_t>(Load8Z, -60));
RUN(testLoad<uint8_t>(Load8Z, 1000));
RUN(testLoad<uint8_t>(Load8Z, -1000));
RUN(testLoad<uint8_t>(Load8Z, 1000000));
RUN(testLoad<uint8_t>(Load8Z, -1000000));
RUN(testLoad<uint8_t>(Load8Z, 1000000000));
RUN(testLoad<uint8_t>(Load8Z, -1000000000));
RUN(testLoad<int16_t>(Load16S, 60));
RUN(testLoad<int16_t>(Load16S, -60));
RUN(testLoad<int16_t>(Load16S, 1000));
RUN(testLoad<int16_t>(Load16S, -1000));
RUN(testLoad<int16_t>(Load16S, 1000000));
RUN(testLoad<int16_t>(Load16S, -1000000));
RUN(testLoad<int16_t>(Load16S, 1000000000));
RUN(testLoad<int16_t>(Load16S, -1000000000));
RUN(testLoad<uint16_t>(Load16Z, 60));
RUN(testLoad<uint16_t>(Load16Z, -60));
RUN(testLoad<uint16_t>(Load16Z, 1000));
RUN(testLoad<uint16_t>(Load16Z, -1000));
RUN(testLoad<uint16_t>(Load16Z, 1000000));
RUN(testLoad<uint16_t>(Load16Z, -1000000));
RUN(testLoad<uint16_t>(Load16Z, 1000000000));
RUN(testLoad<uint16_t>(Load16Z, -1000000000));
}
void testFastForwardCopy32()
{
#if CPU(X86_64)
for (const bool aligned : { true, false }) {
for (const bool overlap : { false, true }) {
for (size_t arrsize : { 1, 4, 5, 6, 8, 10, 12, 16, 20, 40, 100, 1000}) {
size_t overlapAmount = 5;
UniqueArray<uint32_t> array1, array2;
uint32_t* arr1, *arr2;
if (overlap) {
array1 = makeUniqueArray<uint32_t>(arrsize * 2);
arr1 = &array1[0];
arr2 = arr1 + (arrsize - overlapAmount);
} else {
array1 = makeUniqueArray<uint32_t>(arrsize);
array2 = makeUniqueArray<uint32_t>(arrsize);
arr1 = &array1[0];
arr2 = &array2[0];
}
if (!aligned && arrsize < 3)
continue;
if (overlap && arrsize <= overlapAmount + 3)
continue;
if (!aligned) {
++arr1;
++arr2;
arrsize -= 1;
overlapAmount -= 1;
}
for (size_t i = 0; i < arrsize; ++i)
arr1[i] = i;
fastForwardCopy32(arr2, arr1, arrsize);
if (overlap) {
for (size_t i = 0; i < arrsize - overlapAmount; ++i)
CHECK(arr2[i] == i);
for (size_t i = arrsize - overlapAmount; i < arrsize; ++i)
CHECK(arr2[i] == i - (arrsize - overlapAmount));
} else {
for (size_t i = 0; i < arrsize; ++i)
CHECK(arr2[i] == i);
}
if (!aligned) {
--arr1;
--arr2;
}
}
}
}
#endif
}
void testByteCopyLoop()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* head = proc.addBlock();
BasicBlock* update = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
auto* arraySrc = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
auto* arrayDst = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
auto* arraySize = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2));
auto* one = root->appendNew<Const32Value>(proc, Origin(), 1);
auto* two = root->appendNew<Const32Value>(proc, Origin(), 2);
UpsilonValue* startingIndex = root->appendNew<UpsilonValue>(proc, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
root->appendNew<Value>(proc, Jump, Origin());
root->setSuccessors(FrequentedBlock(head));
auto* index = head->appendNew<Value>(proc, Phi, Int32, Origin());
startingIndex->setPhi(index);
auto* loadIndex = head->appendNew<Value>(proc, Add, Origin(), arraySrc,
head->appendNew<Value>(proc, ZExt32, Origin(), head->appendNew<Value>(proc, Shl, Origin(), index, two)));
auto* storeIndex = head->appendNew<Value>(proc, Add, Origin(), arrayDst,
head->appendNew<Value>(proc, ZExt32, Origin(), head->appendNew<Value>(proc, Shl, Origin(), index, two)));
head->appendNew<MemoryValue>(proc, Store, Origin(), head->appendNew<MemoryValue>(proc, Load, Int32, Origin(), loadIndex), storeIndex);
auto* newIndex = head->appendNew<Value>(proc, Add, Origin(), index, one);
auto* cmpValue = head->appendNew<Value>(proc, GreaterThan, Origin(), newIndex, arraySize);
head->appendNew<Value>(proc, Branch, Origin(), cmpValue);
head->setSuccessors(FrequentedBlock(continuation), FrequentedBlock(update));
UpsilonValue* updateIndex = update->appendNew<UpsilonValue>(proc, Origin(), newIndex);
updateIndex->setPhi(index);
update->appendNew<Value>(proc, Jump, Origin());
update->setSuccessors(FrequentedBlock(head));
continuation->appendNewControlValue(proc, Return, Origin());
int* arr1 = new int[3];
int* arr2 = new int[3];
arr1[0] = 0;
arr1[1] = 0;
arr1[2] = 0;
arr2[0] = 1;
arr2[1] = 2;
arr2[2] = 3;
compileAndRun<void>(proc, arr2, arr1, 3);
CHECK_EQ(arr1[0], 1);
CHECK_EQ(arr1[1], 2);
CHECK_EQ(arr1[2], 3);
delete[] arr1;
delete [] arr2;
}
void testByteCopyLoopStartIsLoopDependent()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* head = proc.addBlock();
BasicBlock* update = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
auto* arraySrc = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
auto* arrayDst = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
auto* arraySize = root->appendNew<Value>(proc, Trunc, Origin(), root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2));
auto* one = root->appendNew<Const32Value>(proc, Origin(), 1);
auto* two = root->appendNew<Const32Value>(proc, Origin(), 2);
root->appendNew<Value>(proc, Jump, Origin());
root->setSuccessors(FrequentedBlock(head));
UpsilonValue* startingIndex = head->appendNew<UpsilonValue>(proc, Origin(), head->appendNew<Const32Value>(proc, Origin(), 0));
auto* index = head->appendNew<Value>(proc, Phi, Int32, Origin());
startingIndex->setPhi(index);
auto* loadIndex = head->appendNew<Value>(proc, Add, Origin(), arraySrc,
head->appendNew<Value>(proc, ZExt32, Origin(), head->appendNew<Value>(proc, Shl, Origin(), index, two)));
auto* storeIndex = head->appendNew<Value>(proc, Add, Origin(), arrayDst,
head->appendNew<Value>(proc, ZExt32, Origin(), head->appendNew<Value>(proc, Shl, Origin(), index, two)));
head->appendNew<MemoryValue>(proc, Store, Origin(), head->appendNew<MemoryValue>(proc, Load, Int32, Origin(), loadIndex), storeIndex);
auto* newIndex = head->appendNew<Value>(proc, Add, Origin(), index, one);
auto* cmpValue = head->appendNew<Value>(proc, GreaterThan, Origin(), newIndex, arraySize);
head->appendNew<Value>(proc, Branch, Origin(), cmpValue);
head->setSuccessors(FrequentedBlock(continuation), FrequentedBlock(update));
UpsilonValue* updateIndex = update->appendNew<UpsilonValue>(proc, Origin(), newIndex);
updateIndex->setPhi(index);
update->appendNew<Value>(proc, Jump, Origin());
update->setSuccessors(FrequentedBlock(head));
continuation->appendNewControlValue(proc, Return, Origin());
int* arr1 = new int[3];
int* arr2 = new int[3];
arr1[0] = 0;
arr1[1] = 0;
arr1[2] = 0;
arr2[0] = 1;
arr2[1] = 2;
arr2[2] = 3;
compileAndRun<void>(proc, arr2, arr1, 0);
CHECK_EQ(arr1[0], 1);
CHECK_EQ(arr1[1], 0);
CHECK_EQ(arr1[2], 0);
delete[] arr1;
delete [] arr2;
}
void testByteCopyLoopBoundIsLoopDependent()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* head = proc.addBlock();
BasicBlock* update = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
auto* arraySrc = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
auto* arrayDst = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
auto* one = root->appendNew<Const32Value>(proc, Origin(), 1);
auto* two = root->appendNew<Const32Value>(proc, Origin(), 2);
UpsilonValue* startingIndex = root->appendNew<UpsilonValue>(proc, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
root->appendNew<Value>(proc, Jump, Origin());
root->setSuccessors(FrequentedBlock(head));
auto* index = head->appendNew<Value>(proc, Phi, Int32, Origin());
startingIndex->setPhi(index);
auto* loadIndex = head->appendNew<Value>(proc, Add, Origin(), arraySrc,
head->appendNew<Value>(proc, ZExt32, Origin(), head->appendNew<Value>(proc, Shl, Origin(), index, two)));
auto* storeIndex = head->appendNew<Value>(proc, Add, Origin(), arrayDst,
head->appendNew<Value>(proc, ZExt32, Origin(), head->appendNew<Value>(proc, Shl, Origin(), index, two)));
head->appendNew<MemoryValue>(proc, Store, Origin(), head->appendNew<MemoryValue>(proc, Load, Int32, Origin(), loadIndex), storeIndex);
auto* newIndex = head->appendNew<Value>(proc, Add, Origin(), index, one);
auto* cmpValue = head->appendNew<Value>(proc, GreaterThan, Origin(), newIndex, index);
head->appendNew<Value>(proc, Branch, Origin(), cmpValue);
head->setSuccessors(FrequentedBlock(continuation), FrequentedBlock(update));
UpsilonValue* updateIndex = update->appendNew<UpsilonValue>(proc, Origin(), newIndex);
updateIndex->setPhi(index);
update->appendNew<Value>(proc, Jump, Origin());
update->setSuccessors(FrequentedBlock(head));
continuation->appendNewControlValue(proc, Return, Origin());
int* arr1 = new int[3];
int* arr2 = new int[3];
arr1[0] = 0;
arr1[1] = 0;
arr1[2] = 0;
arr2[0] = 1;
arr2[1] = 2;
arr2[2] = 3;
compileAndRun<void>(proc, arr2, arr1, 3);
CHECK_EQ(arr1[0], 1);
CHECK_EQ(arr1[1], 0);
CHECK_EQ(arr1[2], 0);
delete[] arr1;
delete [] arr2;
}
void addCopyTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
{
RUN(testFastForwardCopy32());
RUN(testByteCopyLoop());
RUN(testByteCopyLoopStartIsLoopDependent());
RUN(testByteCopyLoopBoundIsLoopDependent());
}
#endif // ENABLE(B3_JIT)