blob: 99120a5578894f3eb067ced3fb21e3de70ccad38 [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 (isARM64E())
checkUsesInstruction(compilation, "casal");
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 (isARM64E())
checkUsesInstruction(compilation, "casal");
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);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* ptr = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<AtomicValue>(
proc, AtomicStrongCAS, Origin(), width,
root->appendIntConstant(proc, Origin(), type, 0x0f00000000000000ULL + 42),
root->appendIntConstant(proc, Origin(), type, 0xbeef),
ptr));
auto code = compileProc(proc);
T value[2];
T result;
value[0] = 42;
value[1] = 13;
result = invoke<T>(*code, value);
if (width == Width64)
CHECK_EQ(value[0], static_cast<T>(42));
else
CHECK_EQ(value[0], static_cast<T>(0xbeef));
CHECK_EQ(value[1], 13);
CHECK_EQ(result, static_cast<T>(42));
value[0] = static_cast<T>(300);
result = invoke<T>(*code, value);
CHECK_EQ(value[0], static_cast<T>(300));
CHECK_EQ(value[1], 13);
CHECK_EQ(result, static_cast<T>(300));
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()) {
// AtomicXchg can be lowered to "xchg" without "lock", and this is OK since "lock" signal is asserted for "xchg" by default.
if (AtomicXchg != opcode)
checkUsesInstruction(compilation, "lock");
} else {
if (isARM64E()) {
switch (opcode) {
case AtomicXchgAdd:
checkUsesInstruction(compilation, "ldaddal");
break;
case AtomicXchgAnd:
checkUsesInstruction(compilation, "ldclral");
break;
case AtomicXchgOr:
checkUsesInstruction(compilation, "ldsetal");
break;
case AtomicXchgSub:
checkUsesInstruction(compilation, "ldaddal");
break;
case AtomicXchgXor:
checkUsesInstruction(compilation, "ldeoral");
break;
case AtomicXchg:
checkUsesInstruction(compilation, "swpal");
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
} 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 testWasmAddressDoesNotCSE()
{
Procedure proc;
GPRReg pinnedGPR = GPRInfo::argumentGPR0;
proc.pinRegister(pinnedGPR);
BasicBlock* root = proc.addBlock();
BasicBlock* a = proc.addBlock();
BasicBlock* b = proc.addBlock();
BasicBlock* c = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
auto* pointer = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
auto* path = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
auto* originalAddress = root->appendNew<WasmAddressValue>(proc, Origin(), pointer, pinnedGPR);
root->appendNew<MemoryValue>(proc, Store, Origin(), originalAddress,
root->appendNew<WasmAddressValue>(proc, Origin(), root->appendNew<Const64Value>(proc, Origin(), 6*8), pinnedGPR), 0);
SwitchValue* switchValue = root->appendNew<SwitchValue>(proc, Origin(), path);
switchValue->setFallThrough(FrequentedBlock(c));
switchValue->appendCase(SwitchCase(0, FrequentedBlock(a)));
switchValue->appendCase(SwitchCase(1, FrequentedBlock(b)));
PatchpointValue* patchpoint = b->appendNew<PatchpointValue>(proc, Void, Origin());
patchpoint->effects = Effects::forCall();
patchpoint->clobber(RegisterSet::macroScratchRegisters());
patchpoint->clobber(RegisterSet(pinnedGPR));
patchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
CHECK(!params.size());
jit.add64(MacroAssembler::TrustedImm32(8), pinnedGPR);
});
UpsilonValue* takeA = a->appendNew<UpsilonValue>(proc, Origin(), a->appendNew<Const32Value>(proc, Origin(), 10));
UpsilonValue* takeB = b->appendNew<UpsilonValue>(proc, Origin(), b->appendNew<Const32Value>(proc, Origin(), 20));
UpsilonValue* takeC = c->appendNew<UpsilonValue>(proc, Origin(), c->appendNew<Const32Value>(proc, Origin(), 30));
for (auto* i : { a, b, c }) {
i->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation));
i->setSuccessors(FrequentedBlock(continuation));
}
// Continuation
auto* takenPhi = continuation->appendNew<Value>(proc, Phi, Int32, Origin());
auto* address2 = continuation->appendNew<WasmAddressValue>(proc, Origin(), pointer, pinnedGPR);
continuation->appendNew<MemoryValue>(proc, Store, Origin(), takenPhi,
continuation->appendNew<WasmAddressValue>(proc, Origin(), continuation->appendNew<Const64Value>(proc, Origin(), 4*8), pinnedGPR),
0);
auto* returnVal = address2;
continuation->appendNewControlValue(proc, Return, Origin(), returnVal);
takeA->setPhi(takenPhi);
takeB->setPhi(takenPhi);
takeC->setPhi(takenPhi);
auto binary = compileProc(proc);
uint64_t* memory = new uint64_t[10];
uint64_t ptr = 8;
uint64_t finalPtr = reinterpret_cast<uint64_t>(static_cast<void*>(memory)) + ptr;
for (int i = 0; i < 10; ++i)
memory[i] = 0;
{
uint64_t result = invoke<uint64_t>(*binary, memory, ptr, 0);
CHECK_EQ(result, finalPtr);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 0ul);
CHECK_EQ(memory[2], 0ul);
CHECK_EQ(memory[4], 10ul);
CHECK_EQ(memory[6], finalPtr);
}
memory[4] = 0;
memory[5] = 0;
memory[6] = 0;
memory[7] = 0;
{
uint64_t result = invoke<uint64_t>(*binary, memory, ptr, 1);
CHECK_EQ(result, finalPtr + 8);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 0ul);
CHECK_EQ(memory[2], 0ul);
CHECK_EQ(memory[5], 20ul);
CHECK_EQ(memory[6], finalPtr);
}
memory[4] = 0;
memory[5] = 0;
memory[6] = 0;
memory[7] = 0;
{
uint64_t result = invoke<uint64_t>(*binary, memory, ptr, 2);
CHECK_EQ(result, finalPtr);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 0ul);
CHECK_EQ(memory[2], 0ul);
CHECK_EQ(memory[4], 30ul);
CHECK_EQ(memory[6], finalPtr);
}
delete[] memory;
}
void testStoreAfterClobberExitsSideways()
{
Procedure proc;
GPRReg pinnedBaseGPR = GPRInfo::argumentGPR0;
GPRReg pinnedSizeGPR = GPRInfo::argumentGPR1;
proc.pinRegister(pinnedBaseGPR);
proc.pinRegister(pinnedSizeGPR);
// Please don't make me save anything.
RegisterSet csrs;
csrs.merge(RegisterSet::calleeSaveRegisters());
csrs.exclude(RegisterSet::stackRegisters());
csrs.forEach(
[&] (Reg reg) {
CHECK(reg != pinnedBaseGPR);
CHECK(reg != pinnedSizeGPR);
proc.pinRegister(reg);
});
proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) {
CHECK_EQ(pinnedGPR, pinnedSizeGPR);
jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
BasicBlock* root = proc.addBlock();
auto* pointer = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
auto* resultAddress = root->appendNew<WasmAddressValue>(proc, Origin(), pointer, pinnedBaseGPR);
root->appendNew<MemoryValue>(proc, Store, Origin(), root->appendNew<Const32Value>(proc, Origin(), 10), resultAddress, 0);
root->appendNew<WasmBoundsCheckValue>(proc, Origin(), pinnedSizeGPR, root->appendNew<Value>(proc, Trunc, Origin(), pointer), 0);
root->appendNew<MemoryValue>(proc, Store, Origin(), root->appendNew<Const32Value>(proc, Origin(), 20), resultAddress, 0);
root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 30));
auto binary = compileProc(proc);
uint64_t* memory = new uint64_t[10];
uint64_t ptr = 1*8;
for (int i = 0; i < 10; ++i)
memory[i] = 0;
{
int result = invoke<int>(*binary, memory, 16, ptr);
CHECK_EQ(result, 30);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 20ul);
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, 1, ptr);
CHECK_EQ(result, 42);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 10ul);
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
delete[] memory;
}
void testStoreAfterClobberDifferentWidth()
{
Procedure proc;
GPRReg pinnedBaseGPR = GPRInfo::argumentGPR0;
proc.pinRegister(pinnedBaseGPR);
BasicBlock* root = proc.addBlock();
auto* pointer = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
auto* resultAddress = root->appendNew<WasmAddressValue>(proc, Origin(), pointer, pinnedBaseGPR);
root->appendNew<MemoryValue>(proc, Store, Origin(), root->appendNew<Const64Value>(proc, Origin(), -1), resultAddress, 0);
root->appendNew<MemoryValue>(proc, Store, Origin(), root->appendNew<Const32Value>(proc, Origin(), 20), resultAddress, 0);
root->appendNewControlValue(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 30));
auto binary = compileProc(proc);
uint64_t* memory = new uint64_t[10];
uint64_t ptr = 1*8;
for (int i = 0; i < 10; ++i)
memory[i] = 0;
{
int result = invoke<int>(*binary, memory, ptr);
CHECK_EQ(result, 30);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], (0xFFFFFFFF00000000ul | 20ul));
CHECK_EQ(memory[2], 0ul);
}
delete[] memory;
}
void testStoreAfterClobberDifferentWidthSuccessor()
{
Procedure proc;
GPRReg pinnedBaseGPR = GPRInfo::argumentGPR0;
proc.pinRegister(pinnedBaseGPR);
BasicBlock* root = proc.addBlock();
BasicBlock* a = proc.addBlock();
BasicBlock* b = proc.addBlock();
BasicBlock* c = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
auto* pointer = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
auto* path = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
auto* resultAddress = root->appendNew<WasmAddressValue>(proc, Origin(), pointer, pinnedBaseGPR);
root->appendNew<MemoryValue>(proc, Store, Origin(), root->appendNew<Const64Value>(proc, Origin(), -1), resultAddress, 0);
SwitchValue* switchValue = root->appendNew<SwitchValue>(proc, Origin(), path);
switchValue->setFallThrough(FrequentedBlock(c));
switchValue->appendCase(SwitchCase(0, FrequentedBlock(a)));
switchValue->appendCase(SwitchCase(1, FrequentedBlock(b)));
a->appendNew<MemoryValue>(proc, Store, Origin(), a->appendNew<Const32Value>(proc, Origin(), 10), resultAddress, 0);
b->appendNew<MemoryValue>(proc, Store, Origin(), b->appendNew<Const32Value>(proc, Origin(), 20), resultAddress, 0);
c->appendNew<MemoryValue>(proc, Store, Origin(), c->appendNew<Const32Value>(proc, Origin(), 30), resultAddress, 0);
for (auto* i : { a, b, c }) {
i->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation));
i->setSuccessors(FrequentedBlock(continuation));
}
continuation->appendNewControlValue(proc, Return, Origin(), continuation->appendNew<Const32Value>(proc, Origin(), 40));
auto binary = compileProc(proc);
uint64_t* memory = new uint64_t[10];
uint64_t ptr = 1*8;
for (int i = 0; i < 10; ++i)
memory[i] = 0;
{
int result = invoke<int>(*binary, memory, ptr, 0);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], (0xFFFFFFFF00000000ul | 10ul));
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, ptr, 1);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], (0xFFFFFFFF00000000ul | 20ul));
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, ptr, 2);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], (0xFFFFFFFF00000000ul | 30ul));
CHECK_EQ(memory[2], 0ul);
}
delete[] memory;
}
void testStoreAfterClobberExitsSidewaysSuccessor()
{
Procedure proc;
GPRReg pinnedBaseGPR = GPRInfo::argumentGPR0;
GPRReg pinnedSizeGPR = GPRInfo::argumentGPR1;
proc.pinRegister(pinnedBaseGPR);
proc.pinRegister(pinnedSizeGPR);
// Please don't make me save anything.
RegisterSet csrs;
csrs.merge(RegisterSet::calleeSaveRegisters());
csrs.exclude(RegisterSet::stackRegisters());
csrs.forEach(
[&] (Reg reg) {
CHECK(reg != pinnedBaseGPR);
CHECK(reg != pinnedSizeGPR);
proc.pinRegister(reg);
});
proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR) {
CHECK_EQ(pinnedGPR, pinnedSizeGPR);
jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
BasicBlock* root = proc.addBlock();
BasicBlock* a = proc.addBlock();
BasicBlock* b = proc.addBlock();
BasicBlock* c = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
auto* pointer = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
auto* path = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3);
auto* resultAddress = root->appendNew<WasmAddressValue>(proc, Origin(), pointer, pinnedBaseGPR);
root->appendNew<MemoryValue>(proc, Store, Origin(), root->appendNew<Const64Value>(proc, Origin(), -1), resultAddress, 0);
SwitchValue* switchValue = root->appendNew<SwitchValue>(proc, Origin(), path);
switchValue->setFallThrough(FrequentedBlock(c));
switchValue->appendCase(SwitchCase(0, FrequentedBlock(a)));
switchValue->appendCase(SwitchCase(1, FrequentedBlock(b)));
b->appendNew<WasmBoundsCheckValue>(proc, Origin(), pinnedSizeGPR, b->appendNew<Value>(proc, Trunc, Origin(), pointer), 0);
UpsilonValue* takeA = a->appendNew<UpsilonValue>(proc, Origin(), a->appendNew<Const64Value>(proc, Origin(), 10));
UpsilonValue* takeB = b->appendNew<UpsilonValue>(proc, Origin(), b->appendNew<Const64Value>(proc, Origin(), 20));
UpsilonValue* takeC = c->appendNew<UpsilonValue>(proc, Origin(), c->appendNew<Const64Value>(proc, Origin(), 30));
for (auto* i : { a, b, c }) {
i->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(continuation));
i->setSuccessors(FrequentedBlock(continuation));
}
auto* takenPhi = continuation->appendNew<Value>(proc, Phi, Int64, Origin());
continuation->appendNew<MemoryValue>(proc, Store, Origin(), takenPhi, resultAddress, 0);
continuation->appendNewControlValue(proc, Return, Origin(), continuation->appendNew<Const32Value>(proc, Origin(), 40));
takeA->setPhi(takenPhi);
takeB->setPhi(takenPhi);
takeC->setPhi(takenPhi);
auto binary = compileProc(proc);
uint64_t* memory = new uint64_t[10];
uint64_t ptr = 1*8;
for (int i = 0; i < 10; ++i)
memory[i] = 0;
{
int result = invoke<int>(*binary, memory, 16, ptr, 0);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 10ul);
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, 16, ptr, 1);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 20ul);
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, 16, ptr, 2);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 30ul);
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, 1, ptr, 2);
CHECK_EQ(result, 40);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], 30ul);
CHECK_EQ(memory[2], 0ul);
}
memory[1] = 0;
{
int result = invoke<int>(*binary, memory, 1, ptr, 1);
CHECK_EQ(result, 42);
CHECK_EQ(memory[0], 0ul);
CHECK_EQ(memory[1], (0xFFFFFFFFFFFFFFFFul));
CHECK_EQ(memory[2], 0ul);
}
delete[] memory;
}
void addCopyTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
{
RUN(testFastForwardCopy32());
RUN(testByteCopyLoop());
RUN(testByteCopyLoopStartIsLoopDependent());
RUN(testByteCopyLoopBoundIsLoopDependent());
}
#endif // ENABLE(B3_JIT)