/* * 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 #if ENABLE(B3_JIT) template void testAtomicWeakCAS() { constexpr Type type = NativeTraits::type; constexpr Width width = NativeTraits::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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew(proc, Jump, Origin()); root->setSuccessors(reloop); reloop->appendNew( proc, Branch, Origin(), reloop->appendNew( proc, AtomicWeakCAS, Origin(), width, reloop->appendIntConstant(proc, Origin(), type, 42), reloop->appendIntConstant(proc, Origin(), type, 0xbeef), ptr)); reloop->setSuccessors(done, reloop); done->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; invoke(*code, value); CHECK_EQ(value[0], static_cast(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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew(proc, Jump, Origin()); root->setSuccessors(reloop); reloop->appendNew( proc, Branch, Origin(), reloop->appendNew( 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(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; invoke(*code, value); CHECK_EQ(value[0], static_cast(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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew( proc, Branch, Origin(), root->appendNew( proc, AtomicWeakCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), ptr)); root->setSuccessors(succ, fail); succ->appendNew( proc, storeOpcode(GP, width), Origin(), succ->appendIntConstant(proc, Origin(), type, 100), ptr); succ->appendNew(proc, Return, Origin()); fail->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; while (value[0] == 42) invoke(*code, value); CHECK_EQ(value[0], static_cast(100)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); invoke(*code, value); CHECK_EQ(value[0], static_cast(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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew( proc, Branch, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( 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( proc, storeOpcode(GP, width), Origin(), succ->appendIntConstant(proc, Origin(), type, 100), ptr); succ->appendNew(proc, Return, Origin()); fail->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; while (value[0] == 42) invoke(*code, value); CHECK_EQ(value[0], static_cast(100)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); invoke(*code, value); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, AtomicWeakCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; while (!invoke(*code, value)) { } CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK(!invoke(*code, value)); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( proc, AtomicWeakCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), root->appendNew(proc, Origin(), 0))); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; while (invoke(*code, value)) { } CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK(invoke(*code, value)); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, AtomicWeakCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), 42)); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; while (!invoke(*code, bitwise_cast(value) - 42)) { } CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK(!invoke(*code, bitwise_cast(value) - 42)); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } } template void testAtomicStrongCAS() { constexpr Type type = NativeTraits::type; constexpr Width width = NativeTraits::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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew( proc, Branch, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( 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( proc, storeOpcode(GP, width), Origin(), succ->appendIntConstant(proc, Origin(), type, 100), ptr); succ->appendNew(proc, Return, Origin()); fail->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; invoke(*code, value); CHECK_EQ(value[0], static_cast(100)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); invoke(*code, value); CHECK_EQ(value[0], static_cast(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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew( proc, Branch, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( 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( proc, storeOpcode(GP, width), Origin(), succ->appendIntConstant(proc, Origin(), type, 100), ptr); succ->appendNew(proc, Return, Origin()); fail->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; invoke(*code, value); CHECK_EQ(value[0], static_cast(100)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); invoke(*code, value); CHECK_EQ(value[0], static_cast(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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNew( proc, Branch, Origin(), root->appendNew( proc, NotEqual, Origin(), root->appendNew( 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( proc, storeOpcode(GP, width), Origin(), succ->appendIntConstant(proc, Origin(), type, 100), ptr); succ->appendNew(proc, Return, Origin()); fail->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; invoke(*code, value); CHECK_EQ(value[0], static_cast(100)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); invoke(*code, value); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, AtomicStrongCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; CHECK_EQ(invoke::CanonicalType>(*code, value), 42); CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK_EQ(invoke::CanonicalType>(*code, value), static_cast::CanonicalType>(static_cast(300))); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); value[0] = static_cast(-1); CHECK_EQ(invoke::CanonicalType>(*code, value), static_cast::CanonicalType>(static_cast(-1))); CHECK_EQ(value[0], static_cast(-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( proc, Return, Origin(), root->appendNew( proc, BitXor, Origin(), root->appendNew( proc, AtomicStrongCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), root->appendIntConstant(proc, Origin(), type, 1))); typename NativeTraits::CanonicalType one = 1; auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; CHECK_EQ(invoke::CanonicalType>(*code, value), 42 ^ one); CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK_EQ(invoke::CanonicalType>(*code, value), static_cast::CanonicalType>(static_cast(300)) ^ one); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); value[0] = static_cast(-1); CHECK_EQ(invoke::CanonicalType>(*code, value), static_cast::CanonicalType>(static_cast(-1)) ^ one); CHECK_EQ(value[0], static_cast(-1)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( proc, AtomicStrongCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(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(*code, value)); CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK(!invoke(*code, value)); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( proc, NotEqual, Origin(), root->appendNew( proc, AtomicStrongCAS, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendIntConstant(proc, Origin(), type, 0xbeef), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), root->appendIntConstant(proc, Origin(), type, 42)), root->appendNew(proc, Origin(), 0))); auto code = compileProc(proc); T value[2]; value[0] = 42; value[1] = 13; CHECK(invoke(*code, value)); CHECK_EQ(value[0], static_cast(0xbeef)); CHECK_EQ(value[1], 13); value[0] = static_cast(300); CHECK(!invoke(*code, &value)); CHECK_EQ(value[0], static_cast(300)); CHECK_EQ(value[1], 13); checkMyDisassembly(*code, true); } } template void testAtomicXchg(B3::Opcode opcode) { constexpr Type type = NativeTraits::type; constexpr Width width = NativeTraits::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( proc, Return, Origin(), root->appendNew( proc, opcode, Origin(), width, root->appendIntConstant(proc, Origin(), type, 1), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); auto code = compileProc(proc); T value[2]; value[0] = 5; value[1] = 100; CHECK_EQ(invoke(*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( proc, Return, Origin(), root->appendNew( proc, opcode, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); auto code = compileProc(proc); T value[2]; value[0] = 5; value[1] = 100; CHECK_EQ(invoke(*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( proc, opcode, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); root->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 5; value[1] = 100; invoke(*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( proc, opcode, Origin(), width, root->appendIntConstant(proc, Origin(), type, 42), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), 0, HeapRange(42), HeapRange()); root->appendNew(proc, Return, Origin()); auto code = compileProc(proc); T value[2]; value[0] = 5; value[1] = 100; invoke(*code, value); CHECK_EQ(value[0], newValue(5, 42)); CHECK_EQ(value[1], 100); checkMyDisassembly(*code, false); } } void addAtomicTests(const char* filter, Deque>>& tasks) { RUN(testAtomicWeakCAS()); RUN(testAtomicWeakCAS()); RUN(testAtomicWeakCAS()); RUN(testAtomicWeakCAS()); RUN(testAtomicStrongCAS()); RUN(testAtomicStrongCAS()); RUN(testAtomicStrongCAS()); RUN(testAtomicStrongCAS()); RUN(testAtomicXchg(AtomicXchgAdd)); RUN(testAtomicXchg(AtomicXchgAdd)); RUN(testAtomicXchg(AtomicXchgAdd)); RUN(testAtomicXchg(AtomicXchgAdd)); RUN(testAtomicXchg(AtomicXchgAnd)); RUN(testAtomicXchg(AtomicXchgAnd)); RUN(testAtomicXchg(AtomicXchgAnd)); RUN(testAtomicXchg(AtomicXchgAnd)); RUN(testAtomicXchg(AtomicXchgOr)); RUN(testAtomicXchg(AtomicXchgOr)); RUN(testAtomicXchg(AtomicXchgOr)); RUN(testAtomicXchg(AtomicXchgOr)); RUN(testAtomicXchg(AtomicXchgSub)); RUN(testAtomicXchg(AtomicXchgSub)); RUN(testAtomicXchg(AtomicXchgSub)); RUN(testAtomicXchg(AtomicXchgSub)); RUN(testAtomicXchg(AtomicXchgXor)); RUN(testAtomicXchg(AtomicXchgXor)); RUN(testAtomicXchg(AtomicXchgXor)); RUN(testAtomicXchg(AtomicXchgXor)); RUN(testAtomicXchg(AtomicXchg)); RUN(testAtomicXchg(AtomicXchg)); RUN(testAtomicXchg(AtomicXchg)); RUN(testAtomicXchg(AtomicXchg)); } template 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( proc, opcode, type, Origin(), root->appendNew(proc, Origin(), &value))); CHECK(isIdentical(compileAndRun(proc), modelLoad(value))); } // Simple load from an address in a register. { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, opcode, type, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); CHECK(isIdentical(compileAndRun(proc, &value), modelLoad(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( proc, opcode, type, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), static_cast(sizeof(InputType)))); CHECK(isIdentical(compileAndRun(proc, &value - 1), modelLoad(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( proc, opcode, type, Origin(), root->appendNew( proc, Add, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew( proc, Shl, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1), root->appendNew(proc, Origin(), logScale))))); CHECK(isIdentical(compileAndRun(proc, &value - 2, (sizeof(InputType) * 2) >> logScale), modelLoad(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( proc, opcode, type, Origin(), root->appendNew( proc, Add, Origin(), root->appendNew( proc, Shl, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1), root->appendNew(proc, Origin(), logScale)), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)))); CHECK(isIdentical(compileAndRun(proc, &value - 2, (sizeof(InputType) * 2) >> logScale), modelLoad(value))); } } template void testLoad(B3::Opcode opcode, int32_t value) { return testLoad(B3::Int32, opcode, value); } template void testLoad(B3::Type type, T value) { return testLoad(type, Load, value); } void addLoadTests(const char* filter, Deque>>& 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()); RUN_BINARY(testLoad, { MAKE_OPERAND(Double) }, floatingPointOperands()); RUN(testLoad(Load8S, 60)); RUN(testLoad(Load8S, -60)); RUN(testLoad(Load8S, 1000)); RUN(testLoad(Load8S, -1000)); RUN(testLoad(Load8S, 1000000)); RUN(testLoad(Load8S, -1000000)); RUN(testLoad(Load8S, 1000000000)); RUN(testLoad(Load8S, -1000000000)); RUN(testLoad(Load8Z, 60)); RUN(testLoad(Load8Z, -60)); RUN(testLoad(Load8Z, 1000)); RUN(testLoad(Load8Z, -1000)); RUN(testLoad(Load8Z, 1000000)); RUN(testLoad(Load8Z, -1000000)); RUN(testLoad(Load8Z, 1000000000)); RUN(testLoad(Load8Z, -1000000000)); RUN(testLoad(Load16S, 60)); RUN(testLoad(Load16S, -60)); RUN(testLoad(Load16S, 1000)); RUN(testLoad(Load16S, -1000)); RUN(testLoad(Load16S, 1000000)); RUN(testLoad(Load16S, -1000000)); RUN(testLoad(Load16S, 1000000000)); RUN(testLoad(Load16S, -1000000000)); RUN(testLoad(Load16Z, 60)); RUN(testLoad(Load16Z, -60)); RUN(testLoad(Load16Z, 1000)); RUN(testLoad(Load16Z, -1000)); RUN(testLoad(Load16Z, 1000000)); RUN(testLoad(Load16Z, -1000000)); RUN(testLoad(Load16Z, 1000000000)); RUN(testLoad(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 array1, array2; uint32_t* arr1, *arr2; if (overlap) { array1 = makeUniqueArray(arrsize * 2); arr1 = &array1[0]; arr2 = arr1 + (arrsize - overlapAmount); } else { array1 = makeUniqueArray(arrsize); array2 = makeUniqueArray(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(proc, Origin(), GPRInfo::argumentGPR0); auto* arrayDst = root->appendNew(proc, Origin(), GPRInfo::argumentGPR1); auto* arraySize = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2)); auto* one = root->appendNew(proc, Origin(), 1); auto* two = root->appendNew(proc, Origin(), 2); UpsilonValue* startingIndex = root->appendNew(proc, Origin(), root->appendNew(proc, Origin(), 0)); root->appendNew(proc, Jump, Origin()); root->setSuccessors(FrequentedBlock(head)); auto* index = head->appendNew(proc, Phi, Int32, Origin()); startingIndex->setPhi(index); auto* loadIndex = head->appendNew(proc, Add, Origin(), arraySrc, head->appendNew(proc, ZExt32, Origin(), head->appendNew(proc, Shl, Origin(), index, two))); auto* storeIndex = head->appendNew(proc, Add, Origin(), arrayDst, head->appendNew(proc, ZExt32, Origin(), head->appendNew(proc, Shl, Origin(), index, two))); head->appendNew(proc, Store, Origin(), head->appendNew(proc, Load, Int32, Origin(), loadIndex), storeIndex); auto* newIndex = head->appendNew(proc, Add, Origin(), index, one); auto* cmpValue = head->appendNew(proc, GreaterThan, Origin(), newIndex, arraySize); head->appendNew(proc, Branch, Origin(), cmpValue); head->setSuccessors(FrequentedBlock(continuation), FrequentedBlock(update)); UpsilonValue* updateIndex = update->appendNew(proc, Origin(), newIndex); updateIndex->setPhi(index); update->appendNew(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(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(proc, Origin(), GPRInfo::argumentGPR0); auto* arrayDst = root->appendNew(proc, Origin(), GPRInfo::argumentGPR1); auto* arraySize = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2)); auto* one = root->appendNew(proc, Origin(), 1); auto* two = root->appendNew(proc, Origin(), 2); root->appendNew(proc, Jump, Origin()); root->setSuccessors(FrequentedBlock(head)); UpsilonValue* startingIndex = head->appendNew(proc, Origin(), head->appendNew(proc, Origin(), 0)); auto* index = head->appendNew(proc, Phi, Int32, Origin()); startingIndex->setPhi(index); auto* loadIndex = head->appendNew(proc, Add, Origin(), arraySrc, head->appendNew(proc, ZExt32, Origin(), head->appendNew(proc, Shl, Origin(), index, two))); auto* storeIndex = head->appendNew(proc, Add, Origin(), arrayDst, head->appendNew(proc, ZExt32, Origin(), head->appendNew(proc, Shl, Origin(), index, two))); head->appendNew(proc, Store, Origin(), head->appendNew(proc, Load, Int32, Origin(), loadIndex), storeIndex); auto* newIndex = head->appendNew(proc, Add, Origin(), index, one); auto* cmpValue = head->appendNew(proc, GreaterThan, Origin(), newIndex, arraySize); head->appendNew(proc, Branch, Origin(), cmpValue); head->setSuccessors(FrequentedBlock(continuation), FrequentedBlock(update)); UpsilonValue* updateIndex = update->appendNew(proc, Origin(), newIndex); updateIndex->setPhi(index); update->appendNew(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(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(proc, Origin(), GPRInfo::argumentGPR0); auto* arrayDst = root->appendNew(proc, Origin(), GPRInfo::argumentGPR1); auto* one = root->appendNew(proc, Origin(), 1); auto* two = root->appendNew(proc, Origin(), 2); UpsilonValue* startingIndex = root->appendNew(proc, Origin(), root->appendNew(proc, Origin(), 0)); root->appendNew(proc, Jump, Origin()); root->setSuccessors(FrequentedBlock(head)); auto* index = head->appendNew(proc, Phi, Int32, Origin()); startingIndex->setPhi(index); auto* loadIndex = head->appendNew(proc, Add, Origin(), arraySrc, head->appendNew(proc, ZExt32, Origin(), head->appendNew(proc, Shl, Origin(), index, two))); auto* storeIndex = head->appendNew(proc, Add, Origin(), arrayDst, head->appendNew(proc, ZExt32, Origin(), head->appendNew(proc, Shl, Origin(), index, two))); head->appendNew(proc, Store, Origin(), head->appendNew(proc, Load, Int32, Origin(), loadIndex), storeIndex); auto* newIndex = head->appendNew(proc, Add, Origin(), index, one); auto* cmpValue = head->appendNew(proc, GreaterThan, Origin(), newIndex, index); head->appendNew(proc, Branch, Origin(), cmpValue); head->setSuccessors(FrequentedBlock(continuation), FrequentedBlock(update)); UpsilonValue* updateIndex = update->appendNew(proc, Origin(), newIndex); updateIndex->setPhi(index); update->appendNew(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(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>>& tasks) { RUN(testFastForwardCopy32()); RUN(testByteCopyLoop()); RUN(testByteCopyLoopStartIsLoopDependent()); RUN(testByteCopyLoopBoundIsLoopDependent()); } #endif // ENABLE(B3_JIT)