mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2024-11-26 21:50:53 +00:00
1108 lines
41 KiB
C++
1108 lines
41 KiB
C++
|
/*
|
||
|
* 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()) {
|
||
|
// 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 (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)
|