blob: 76c8366f4ee10ec406f4fb80be6fbd339c11a3bf [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"
#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));
}
#endif // ENABLE(B3_JIT)