blob: b6c6e2ca1fec0f88173779cafa658be477a9e93c [file] [log] [blame]
/*
* Copyright (C) 2015-2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "testb3.h"
#if ENABLE(B3_JIT)
void testBitAndSExt32(int32_t value, int64_t mask)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, SExt32, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))),
root->appendNew<Const64Value>(proc, Origin(), mask)));
CHECK(compileAndRun<int64_t>(proc, value) == (static_cast<int64_t>(value) & mask));
}
void testBasicSelect()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ConstPtrValue>(proc, Origin(), 42)),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
auto code = compileProc(proc);
CHECK(invoke<intptr_t>(*code, 42, 1, 2) == 1);
CHECK(invoke<intptr_t>(*code, 42, 642462, 32533) == 642462);
CHECK(invoke<intptr_t>(*code, 43, 1, 2) == 2);
CHECK(invoke<intptr_t>(*code, 43, 642462, 32533) == 32533);
}
void testSelectTest()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
auto code = compileProc(proc);
CHECK(invoke<intptr_t>(*code, 42, 1, 2) == 1);
CHECK(invoke<intptr_t>(*code, 42, 642462, 32533) == 642462);
CHECK(invoke<intptr_t>(*code, 0, 1, 2) == 2);
CHECK(invoke<intptr_t>(*code, 0, 642462, 32533) == 32533);
}
void testSelectCompareDouble()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, LessThan, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1)),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
auto code = compileProc(proc);
CHECK(invoke<intptr_t>(*code, -1.0, 1.0, 1, 2) == 1);
CHECK(invoke<intptr_t>(*code, 42.5, 42.51, 642462, 32533) == 642462);
CHECK(invoke<intptr_t>(*code, PNaN, 0.0, 1, 2) == 2);
CHECK(invoke<intptr_t>(*code, 42.51, 42.5, 642462, 32533) == 32533);
CHECK(invoke<intptr_t>(*code, 42.52, 42.52, 524978245, 352) == 352);
}
template<B3::Opcode opcode>
void testSelectCompareFloat(float a, float b, bool (*operation)(float, float))
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* argument1int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* argument2int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
Value* floatValue1 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument1int32);
Value* floatValue2 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument2int32);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, opcode, Origin(),
floatValue1,
floatValue2),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3)));
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b), 42, -5), operation(a, b) ? 42 : -5));
}
void testSelectCompareFloat(float a, float b)
{
testSelectCompareFloat<Equal>(a, b, [](float a, float b) -> bool { return a == b; });
testSelectCompareFloat<NotEqual>(a, b, [](float a, float b) -> bool { return a != b; });
testSelectCompareFloat<LessThan>(a, b, [](float a, float b) -> bool { return a < b; });
testSelectCompareFloat<GreaterThan>(a, b, [](float a, float b) -> bool { return a > b; });
testSelectCompareFloat<LessEqual>(a, b, [](float a, float b) -> bool { return a <= b; });
testSelectCompareFloat<GreaterEqual>(a, b, [](float a, float b) -> bool { return a >= b; });
testSelectCompareFloat<EqualOrUnordered>(a, b, [](float a, float b) -> bool { return a != a || b != b || a == b; });
}
template<B3::Opcode opcode>
void testSelectCompareFloatToDouble(float a, float b, bool (*operation)(float, float))
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* argument1int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* argument2int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
Value* floatValue1 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument1int32);
Value* floatValue2 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument2int32);
Value* doubleValue1 = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue1);
Value* doubleValue2 = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue2);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, opcode, Origin(),
doubleValue1,
doubleValue2),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3)));
CHECK(isIdentical(compileAndRun<int32_t>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b), 42, -5), operation(a, b) ? 42 : -5));
}
void testSelectCompareFloatToDouble(float a, float b)
{
testSelectCompareFloatToDouble<Equal>(a, b, [](float a, float b) -> bool { return a == b; });
testSelectCompareFloatToDouble<NotEqual>(a, b, [](float a, float b) -> bool { return a != b; });
testSelectCompareFloatToDouble<LessThan>(a, b, [](float a, float b) -> bool { return a < b; });
testSelectCompareFloatToDouble<GreaterThan>(a, b, [](float a, float b) -> bool { return a > b; });
testSelectCompareFloatToDouble<LessEqual>(a, b, [](float a, float b) -> bool { return a <= b; });
testSelectCompareFloatToDouble<GreaterEqual>(a, b, [](float a, float b) -> bool { return a >= b; });
testSelectCompareFloatToDouble<EqualOrUnordered>(a, b, [](float a, float b) -> bool { return a != a || b != b || a == b; });
}
void testSelectDouble()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ConstPtrValue>(proc, Origin(), 42)),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1)));
auto code = compileProc(proc);
CHECK(invoke<double>(*code, 42, 1.5, 2.6) == 1.5);
CHECK(invoke<double>(*code, 42, 642462.7, 32533.8) == 642462.7);
CHECK(invoke<double>(*code, 43, 1.9, 2.0) == 2.0);
CHECK(invoke<double>(*code, 43, 642462.1, 32533.2) == 32533.2);
}
void testSelectDoubleTest()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1)));
auto code = compileProc(proc);
CHECK(invoke<double>(*code, 42, 1.5, 2.6) == 1.5);
CHECK(invoke<double>(*code, 42, 642462.7, 32533.8) == 642462.7);
CHECK(invoke<double>(*code, 0, 1.9, 2.0) == 2.0);
CHECK(invoke<double>(*code, 0, 642462.1, 32533.2) == 32533.2);
}
void testSelectDoubleCompareDouble()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, LessThan, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1)),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR3)));
auto code = compileProc(proc);
CHECK(invoke<double>(*code, -1.0, 1.0, 1.1, 2.2) == 1.1);
CHECK(invoke<double>(*code, 42.5, 42.51, 642462.3, 32533.4) == 642462.3);
CHECK(invoke<double>(*code, PNaN, 0.0, 1.5, 2.6) == 2.6);
CHECK(invoke<double>(*code, 42.51, 42.5, 642462.7, 32533.8) == 32533.8);
CHECK(invoke<double>(*code, 42.52, 42.52, 524978245.9, 352.0) == 352.0);
}
void testSelectDoubleCompareFloat(float a, float b)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* argument1int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* argument2int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
Value* floatValue1 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument1int32);
Value* floatValue2 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument2int32);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, LessThan, Origin(),
floatValue1,
floatValue2),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1)));
CHECK(isIdentical(compileAndRun<double>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b), 42.1, -M_PI), a < b ? 42.1 : -M_PI));
}
void testSelectFloatCompareFloat(float a, float b)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* argument1int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* argument2int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
Value* argument3int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2));
Value* argument4int32 = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3));
Value* floatValue1 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument1int32);
Value* floatValue2 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument2int32);
Value* floatValue3 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument3int32);
Value* floatValue4 = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument4int32);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, LessThan, Origin(),
floatValue1,
floatValue2),
floatValue3,
floatValue4));
CHECK(isIdentical(compileAndRun<float>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b), bitwise_cast<int32_t>(1.1f), bitwise_cast<int32_t>(-42.f)), a < b ? 1.1f : -42.f));
}
template<B3::Opcode opcode>
void testSelectDoubleCompareDouble(bool (*operation)(double, double))
{
{ // Compare arguments and selected arguments are all different.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2);
Value* arg3 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR3);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, opcode, Origin(),
arg0,
arg1),
arg2,
arg3));
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<double>()) {
for (auto& right : floatingPointOperands<double>()) {
double expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<double>(*code, left.value, right.value, 42.5, -66.5), expected));
}
}
}
{ // Compare arguments and selected arguments are all different. "thenCase" is live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2);
Value* arg3 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR3);
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg3);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<double>()) {
for (auto& right : floatingPointOperands<double>()) {
double expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<double>(*code, left.value, right.value, 42.5, -66.5), expected));
}
}
}
{ // Compare arguments and selected arguments are all different. "elseCase" is live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2);
Value* arg3 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR3);
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg3);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg3, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<double>()) {
for (auto& right : floatingPointOperands<double>()) {
double expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<double>(*code, left.value, right.value, 42.5, -66.5), expected));
}
}
}
{ // Compare arguments and selected arguments are all different. Both cases are live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2);
Value* arg3 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR3);
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg3);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
keepValuesLive->append(ConstrainedValue(arg3, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<double>()) {
for (auto& right : floatingPointOperands<double>()) {
double expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<double>(*code, left.value, right.value, 42.5, -66.5), expected));
}
}
}
{ // The left argument is the same as the "elseCase" argument.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, opcode, Origin(),
arg0,
arg1),
arg2,
arg0));
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<double>()) {
for (auto& right : floatingPointOperands<double>()) {
double expected = operation(left.value, right.value) ? 42.5 : left.value;
CHECK(isIdentical(invoke<double>(*code, left.value, right.value, 42.5, left.value), expected));
}
}
}
{ // The left argument is the same as the "elseCase" argument. "thenCase" is live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR1);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR2);
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg0);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<double>()) {
for (auto& right : floatingPointOperands<double>()) {
double expected = operation(left.value, right.value) ? 42.5 : left.value;
CHECK(isIdentical(invoke<double>(*code, left.value, right.value, 42.5, left.value), expected));
}
}
}
}
void testSelectDoubleCompareDoubleWithAliasing()
{
testSelectDoubleCompareDouble<Equal>([](double a, double b) -> bool { return a == b; });
testSelectDoubleCompareDouble<NotEqual>([](double a, double b) -> bool { return a != b; });
testSelectDoubleCompareDouble<LessThan>([](double a, double b) -> bool { return a < b; });
testSelectDoubleCompareDouble<GreaterThan>([](double a, double b) -> bool { return a > b; });
testSelectDoubleCompareDouble<LessEqual>([](double a, double b) -> bool { return a <= b; });
testSelectDoubleCompareDouble<GreaterEqual>([](double a, double b) -> bool { return a >= b; });
testSelectDoubleCompareDouble<EqualOrUnordered>([](double a, double b) -> bool { return a != a || b != b || a == b; });
}
template<B3::Opcode opcode>
void testSelectFloatCompareFloat(bool (*operation)(float, float))
{
{ // Compare arguments and selected arguments are all different.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
Value* arg1 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
Value* arg2 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
Value* arg3 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3)));
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, opcode, Origin(),
arg0,
arg1),
arg2,
arg3));
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<float>()) {
for (auto& right : floatingPointOperands<float>()) {
float expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<float>(*code, bitwise_cast<int32_t>(left.value), bitwise_cast<int32_t>(right.value), bitwise_cast<int32_t>(42.5f), bitwise_cast<int32_t>(-66.5f)), expected));
}
}
}
{ // Compare arguments and selected arguments are all different. "thenCase" is live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
Value* arg1 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
Value* arg2 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
Value* arg3 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3)));
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg3);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<float>()) {
for (auto& right : floatingPointOperands<float>()) {
float expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<float>(*code, bitwise_cast<int32_t>(left.value), bitwise_cast<int32_t>(right.value), bitwise_cast<int32_t>(42.5f), bitwise_cast<int32_t>(-66.5f)), expected));
}
}
}
{ // Compare arguments and selected arguments are all different. "elseCase" is live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
Value* arg1 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
Value* arg2 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
Value* arg3 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3)));
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg3);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg3, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<float>()) {
for (auto& right : floatingPointOperands<float>()) {
float expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<float>(*code, bitwise_cast<int32_t>(left.value), bitwise_cast<int32_t>(right.value), bitwise_cast<int32_t>(42.5f), bitwise_cast<int32_t>(-66.5f)), expected));
}
}
}
{ // Compare arguments and selected arguments are all different. Both cases are live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
Value* arg1 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
Value* arg2 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
Value* arg3 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3)));
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg3);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
keepValuesLive->append(ConstrainedValue(arg3, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<float>()) {
for (auto& right : floatingPointOperands<float>()) {
float expected = operation(left.value, right.value) ? 42.5 : -66.5;
CHECK(isIdentical(invoke<float>(*code, bitwise_cast<int32_t>(left.value), bitwise_cast<int32_t>(right.value), bitwise_cast<int32_t>(42.5f), bitwise_cast<int32_t>(-66.5f)), expected));
}
}
}
{ // The left argument is the same as the "elseCase" argument.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
Value* arg1 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
Value* arg2 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, opcode, Origin(),
arg0,
arg1),
arg2,
arg0));
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<float>()) {
for (auto& right : floatingPointOperands<float>()) {
float expected = operation(left.value, right.value) ? 42.5 : left.value;
CHECK(isIdentical(invoke<float>(*code, bitwise_cast<int32_t>(left.value), bitwise_cast<int32_t>(right.value), bitwise_cast<int32_t>(42.5f), bitwise_cast<int32_t>(left.value)), expected));
}
}
}
{ // The left argument is the same as the "elseCase" argument. "thenCase" is live after operation.
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg0 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
Value* arg1 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
Value* arg2 = root->appendNew<Value>(proc, BitwiseCast, Origin(),
root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
Value* result = root->appendNew<Value>(proc, Select, Origin(),
root->appendNew<Value>(proc, opcode, Origin(), arg0, arg1),
arg2,
arg0);
PatchpointValue* keepValuesLive = root->appendNew<PatchpointValue>(proc, Void, Origin());
keepValuesLive->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
keepValuesLive->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
for (auto& left : floatingPointOperands<float>()) {
for (auto& right : floatingPointOperands<float>()) {
float expected = operation(left.value, right.value) ? 42.5 : left.value;
CHECK(isIdentical(invoke<float>(*code, bitwise_cast<int32_t>(left.value), bitwise_cast<int32_t>(right.value), bitwise_cast<int32_t>(42.5f), bitwise_cast<int32_t>(left.value)), expected));
}
}
}
}
void testSelectFloatCompareFloatWithAliasing()
{
testSelectFloatCompareFloat<Equal>([](float a, float b) -> bool { return a == b; });
testSelectFloatCompareFloat<NotEqual>([](float a, float b) -> bool { return a != b; });
testSelectFloatCompareFloat<LessThan>([](float a, float b) -> bool { return a < b; });
testSelectFloatCompareFloat<GreaterThan>([](float a, float b) -> bool { return a > b; });
testSelectFloatCompareFloat<LessEqual>([](float a, float b) -> bool { return a <= b; });
testSelectFloatCompareFloat<GreaterEqual>([](float a, float b) -> bool { return a >= b; });
testSelectFloatCompareFloat<EqualOrUnordered>([](float a, float b) -> bool { return a != a || b != b || a == b; });
}
void testSelectFold(intptr_t value)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<ConstPtrValue>(proc, Origin(), value),
root->appendNew<ConstPtrValue>(proc, Origin(), 42)),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
auto code = compileProc(proc);
CHECK(invoke<intptr_t>(*code, 1, 2) == (value == 42 ? 1 : 2));
CHECK(invoke<intptr_t>(*code, 642462, 32533) == (value == 42 ? 642462 : 32533));
}
void testSelectInvert()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, Equal, Origin(),
root->appendNew<Value>(
proc, NotEqual, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ConstPtrValue>(proc, Origin(), 42)),
root->appendNew<Const32Value>(proc, Origin(), 0)),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2)));
auto code = compileProc(proc);
CHECK(invoke<intptr_t>(*code, 42, 1, 2) == 1);
CHECK(invoke<intptr_t>(*code, 42, 642462, 32533) == 642462);
CHECK(invoke<intptr_t>(*code, 43, 1, 2) == 2);
CHECK(invoke<intptr_t>(*code, 43, 642462, 32533) == 32533);
}
void testCheckSelect()
{
Procedure proc;
if (proc.optLevel() < 1)
return;
BasicBlock* root = proc.addBlock();
CheckValue* check = root->appendNew<CheckValue>(
proc, Check, Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(
proc, Origin(), GPRInfo::argumentGPR0)),
root->appendNew<Const32Value>(proc, Origin(), 0xff)),
root->appendNew<ConstPtrValue>(proc, Origin(), -42),
root->appendNew<ConstPtrValue>(proc, Origin(), 35)),
root->appendNew<ConstPtrValue>(proc, Origin(), 42)));
unsigned generationCount = 0;
check->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
generationCount++;
jit.move(CCallHelpers::TrustedImm32(666), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Const32Value>(proc, Origin(), 0));
auto code = compileProc(proc);
CHECK(generationCount == 1);
CHECK(invoke<int>(*code, true) == 0);
CHECK(invoke<int>(*code, false) == 666);
}
void testCheckSelectCheckSelect()
{
Procedure proc;
if (proc.optLevel() < 1)
return;
BasicBlock* root = proc.addBlock();
CheckValue* check = root->appendNew<CheckValue>(
proc, Check, Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(
proc, Origin(), GPRInfo::argumentGPR0)),
root->appendNew<Const32Value>(proc, Origin(), 0xff)),
root->appendNew<ConstPtrValue>(proc, Origin(), -42),
root->appendNew<ConstPtrValue>(proc, Origin(), 35)),
root->appendNew<ConstPtrValue>(proc, Origin(), 42)));
unsigned generationCount = 0;
check->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
generationCount++;
jit.move(CCallHelpers::TrustedImm32(666), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
CheckValue* check2 = root->appendNew<CheckValue>(
proc, Check, Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(
proc, Origin(), GPRInfo::argumentGPR1)),
root->appendNew<Const32Value>(proc, Origin(), 0xff)),
root->appendNew<ConstPtrValue>(proc, Origin(), -43),
root->appendNew<ConstPtrValue>(proc, Origin(), 36)),
root->appendNew<ConstPtrValue>(proc, Origin(), 43)));
unsigned generationCount2 = 0;
check2->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
generationCount2++;
jit.move(CCallHelpers::TrustedImm32(667), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Const32Value>(proc, Origin(), 0));
auto code = compileProc(proc);
CHECK(generationCount == 1);
CHECK(generationCount2 == 1);
CHECK(invoke<int>(*code, true, true) == 0);
CHECK(invoke<int>(*code, false, true) == 666);
CHECK(invoke<int>(*code, true, false) == 667);
}
void testCheckSelectAndCSE()
{
Procedure proc;
if (proc.optLevel() < 1)
return;
BasicBlock* root = proc.addBlock();
auto* selectValue = root->appendNew<Value>(
proc, Select, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(
proc, Origin(), GPRInfo::argumentGPR0)),
root->appendNew<Const32Value>(proc, Origin(), 0xff)),
root->appendNew<ConstPtrValue>(proc, Origin(), -42),
root->appendNew<ConstPtrValue>(proc, Origin(), 35));
auto* constant = root->appendNew<ConstPtrValue>(proc, Origin(), 42);
auto* addValue = root->appendNew<Value>(proc, Add, Origin(), selectValue, constant);
CheckValue* check = root->appendNew<CheckValue>(proc, Check, Origin(), addValue);
unsigned generationCount = 0;
check->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
generationCount++;
jit.move(CCallHelpers::TrustedImm32(666), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
auto* addValue2 = root->appendNew<Value>(proc, Add, Origin(), selectValue, constant);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(proc, Add, Origin(), addValue, addValue2));
auto code = compileProc(proc);
CHECK(generationCount == 1);
CHECK(invoke<int>(*code, true) == 0);
CHECK(invoke<int>(*code, false) == 666);
}
double b3Pow(double x, int y)
{
if (y < 0 || y > 1000)
return pow(x, y);
double result = 1;
while (y) {
if (y & 1)
result *= x;
x *= x;
y >>= 1;
}
return result;
}
void testPowDoubleByIntegerLoop(double xOperand, int32_t yOperand)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* x = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
Value* y = root->appendNew<Value>(proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
auto result = powDoubleInt32(proc, root, Origin(), x, y);
BasicBlock* continuation = result.first;
continuation->appendNewControlValue(proc, Return, Origin(), result.second);
CHECK(isIdentical(compileAndRun<double>(proc, xOperand, yOperand), b3Pow(xOperand, yOperand)));
}
void testTruncOrHigh()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<Value>(
proc, BitOr, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Const64Value>(proc, Origin(), 0x100000000))));
int64_t value = 0x123456781234;
CHECK(compileAndRun<int>(proc, value) == 0x56781234);
}
void testTruncOrLow()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<Value>(
proc, BitOr, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Const64Value>(proc, Origin(), 0x1000000))));
int64_t value = 0x123456781234;
CHECK(compileAndRun<int>(proc, value) == 0x57781234);
}
void testBitAndOrHigh()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, BitOr, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Const64Value>(proc, Origin(), 0x8)),
root->appendNew<Const64Value>(proc, Origin(), 0x777777777777)));
int64_t value = 0x123456781234;
CHECK(compileAndRun<int64_t>(proc, value) == 0x123456701234ll);
}
void testBitAndOrLow()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<Value>(
proc, BitOr, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Const64Value>(proc, Origin(), 0x1)),
root->appendNew<Const64Value>(proc, Origin(), 0x777777777777)));
int64_t value = 0x123456781234;
CHECK(compileAndRun<int64_t>(proc, value) == 0x123456701235ll);
}
void testBranch64Equal(int64_t left, int64_t right)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* thenCase = proc.addBlock();
BasicBlock* elseCase = proc.addBlock();
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
root->appendNewControlValue(
proc, Branch, Origin(),
root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2),
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
bool trueResult = true;
thenCase->appendNewControlValue(
proc, Return, Origin(),
thenCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
thenCase->appendNew<ConstPtrValue>(proc, Origin(), &trueResult)));
bool elseResult = false;
elseCase->appendNewControlValue(
proc, Return, Origin(),
elseCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
elseCase->appendNew<ConstPtrValue>(proc, Origin(), &elseResult)));
CHECK(compileAndRun<bool>(proc, left, right) == (left == right));
}
void testBranch64EqualImm(int64_t left, int64_t right)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* thenCase = proc.addBlock();
BasicBlock* elseCase = proc.addBlock();
Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
Value* arg2 = root->appendNew<ConstPtrValue>(proc, Origin(), right);
root->appendNewControlValue(
proc, Branch, Origin(),
root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2),
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
bool trueResult = true;
thenCase->appendNewControlValue(
proc, Return, Origin(),
thenCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
thenCase->appendNew<ConstPtrValue>(proc, Origin(), &trueResult)));
bool elseResult = false;
elseCase->appendNewControlValue(
proc, Return, Origin(),
elseCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
elseCase->appendNew<ConstPtrValue>(proc, Origin(), &elseResult)));
CHECK(compileAndRun<bool>(proc, left) == (left == right));
}
void testBranch64EqualMem(int64_t left, int64_t right)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* thenCase = proc.addBlock();
BasicBlock* elseCase = proc.addBlock();
Value* arg1 = root->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
root->appendNewControlValue(
proc, Branch, Origin(),
root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2),
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
bool trueResult = true;
thenCase->appendNewControlValue(
proc, Return, Origin(),
thenCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
thenCase->appendNew<ConstPtrValue>(proc, Origin(), &trueResult)));
bool elseResult = false;
elseCase->appendNewControlValue(
proc, Return, Origin(),
elseCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
elseCase->appendNew<ConstPtrValue>(proc, Origin(), &elseResult)));
CHECK(compileAndRun<bool>(proc, &left, right) == (left == right));
}
void testBranch64EqualMemImm(int64_t left, int64_t right)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* thenCase = proc.addBlock();
BasicBlock* elseCase = proc.addBlock();
Value* arg1 = root->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* arg2 = root->appendNew<ConstPtrValue>(proc, Origin(), right);
root->appendNewControlValue(
proc, Branch, Origin(),
root->appendNew<Value>(proc, Equal, Origin(), arg1, arg2),
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
bool trueResult = true;
thenCase->appendNewControlValue(
proc, Return, Origin(),
thenCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
thenCase->appendNew<ConstPtrValue>(proc, Origin(), &trueResult)));
bool elseResult = false;
elseCase->appendNewControlValue(
proc, Return, Origin(),
elseCase->appendNew<MemoryValue>(
proc, Load8Z, Origin(),
elseCase->appendNew<ConstPtrValue>(proc, Origin(), &elseResult)));
CHECK(compileAndRun<bool>(proc, &left) == (left == right));
}
void testStore8Load8Z(int32_t value)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int8_t byte;
Value* ptr = root->appendNew<ConstPtrValue>(proc, Origin(), &byte);
root->appendNew<MemoryValue>(
proc, Store8, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
ptr);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(proc, Load8Z, Origin(), ptr));
CHECK(compileAndRun<int32_t>(proc, value) == static_cast<uint8_t>(value));
}
void testStore16Load16Z(int32_t value)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int16_t byte;
Value* ptr = root->appendNew<ConstPtrValue>(proc, Origin(), &byte);
root->appendNew<MemoryValue>(
proc, Store16, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
ptr);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<MemoryValue>(proc, Load16Z, Origin(), ptr));
CHECK(compileAndRun<int32_t>(proc, value) == static_cast<uint16_t>(value));
}
static void testSShrShl32(int32_t value, int32_t sshrAmount, int32_t shlAmount)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, SShr, Origin(),
root->appendNew<Value>(
proc, Shl, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
root->appendNew<Const32Value>(proc, Origin(), shlAmount)),
root->appendNew<Const32Value>(proc, Origin(), sshrAmount)));
CHECK(
compileAndRun<int32_t>(proc, value)
== ((value << (shlAmount & 31)) >> (sshrAmount & 31)));
}
static void testSShrShl64(int64_t value, int32_t sshrAmount, int32_t shlAmount)
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, SShr, Origin(),
root->appendNew<Value>(
proc, Shl, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Const32Value>(proc, Origin(), shlAmount)),
root->appendNew<Const32Value>(proc, Origin(), sshrAmount)));
CHECK(
compileAndRun<int64_t>(proc, value)
== ((value << (shlAmount & 63)) >> (sshrAmount & 63)));
}
void testTrivialInfiniteLoop()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* loop = proc.addBlock();
root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loop));
loop->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(loop));
compileProc(proc);
}
void testFoldPathEqual()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* thenBlock = proc.addBlock();
BasicBlock* elseBlock = proc.addBlock();
Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNewControlValue(
proc, Branch, Origin(), arg, FrequentedBlock(thenBlock), FrequentedBlock(elseBlock));
thenBlock->appendNewControlValue(
proc, Return, Origin(),
thenBlock->appendNew<Value>(
proc, Equal, Origin(), arg, thenBlock->appendNew<ConstPtrValue>(proc, Origin(), 0)));
elseBlock->appendNewControlValue(
proc, Return, Origin(),
elseBlock->appendNew<Value>(
proc, Equal, Origin(), arg, elseBlock->appendNew<ConstPtrValue>(proc, Origin(), 0)));
auto code = compileProc(proc);
CHECK(invoke<intptr_t>(*code, 0) == 1);
CHECK(invoke<intptr_t>(*code, 1) == 0);
CHECK(invoke<intptr_t>(*code, 42) == 0);
}
void testLShiftSelf32()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(proc, Shl, Origin(), arg, arg));
auto code = compileProc(proc);
auto check = [&] (int32_t value) {
CHECK(invoke<int32_t>(*code, value) == value << (value & 31));
};
check(0);
check(1);
check(31);
check(32);
}
void testRShiftSelf32()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(proc, SShr, Origin(), arg, arg));
auto code = compileProc(proc);
auto check = [&] (int32_t value) {
CHECK(invoke<int32_t>(*code, value) == value >> (value & 31));
};
check(0);
check(1);
check(31);
check(32);
}
void testURShiftSelf32()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(proc, ZShr, Origin(), arg, arg));
auto code = compileProc(proc);
auto check = [&] (uint32_t value) {
CHECK(invoke<uint32_t>(*code, value) == value >> (value & 31));
};
check(0);
check(1);
check(31);
check(32);
}
void testLShiftSelf64()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Shl, Origin(), arg, root->appendNew<Value>(proc, Trunc, Origin(), arg)));
auto code = compileProc(proc);
auto check = [&] (int64_t value) {
CHECK(invoke<int64_t>(*code, value) == value << (value & 63));
};
check(0);
check(1);
check(31);
check(32);
check(63);
check(64);
}
void testRShiftSelf64()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, SShr, Origin(), arg, root->appendNew<Value>(proc, Trunc, Origin(), arg)));
auto code = compileProc(proc);
auto check = [&] (int64_t value) {
CHECK(invoke<int64_t>(*code, value) == value >> (value & 63));
};
check(0);
check(1);
check(31);
check(32);
check(63);
check(64);
}
void testURShiftSelf64()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
root->appendNewControlValue(
proc, Return, Origin(),
root->appendNew<Value>(
proc, ZShr, Origin(), arg, root->appendNew<Value>(proc, Trunc, Origin(), arg)));
auto code = compileProc(proc);
auto check = [&] (uint64_t value) {
CHECK(invoke<uint64_t>(*code, value) == value >> (value & 63));
};
check(0);
check(1);
check(31);
check(32);
check(63);
check(64);
}
void testPatchpointDoubleRegs()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* arg = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Double, Origin());
patchpoint->append(arg, ValueRep(FPRInfo::fpRegT0));
patchpoint->resultConstraints = { ValueRep(FPRInfo::fpRegT0) };
unsigned numCalls = 0;
patchpoint->setGenerator(
[&] (CCallHelpers&, const StackmapGenerationParams&) {
numCalls++;
});
root->appendNewControlValue(proc, Return, Origin(), patchpoint);
auto code = compileProc(proc);
CHECK(numCalls == 1);
CHECK(invoke<double>(*code, 42.5) == 42.5);
}
void testSpillDefSmallerThanUse()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
// Move32.
Value* arg32 = root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
Value* arg64 = root->appendNew<Value>(proc, ZExt32, Origin(), arg32);
// Make sure arg64 is on the stack.
PatchpointValue* forceSpill = root->appendNew<PatchpointValue>(proc, Int64, Origin());
RegisterSet clobberSet = RegisterSet::allGPRs();
clobberSet.exclude(RegisterSet::stackRegisters());
clobberSet.exclude(RegisterSet::reservedHardwareRegisters());
clobberSet.clear(GPRInfo::returnValueGPR); // Force the return value for aliasing below.
forceSpill->clobberLate(clobberSet);
forceSpill->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
jit.xor64(params[0].gpr(), params[0].gpr());
});
// On x86, Sub admit an address for any operand. If it uses the stack, the top bits must be zero.
Value* result = root->appendNew<Value>(proc, Sub, Origin(), forceSpill, arg64);
root->appendNewControlValue(proc, Return, Origin(), result);
auto code = compileProc(proc);
CHECK(invoke<int64_t>(*code, 0xffffffff00000000) == 0);
}
void testSpillUseLargerThanDef()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* thenCase = proc.addBlock();
BasicBlock* elseCase = proc.addBlock();
BasicBlock* tail = proc.addBlock();
RegisterSet clobberSet = RegisterSet::allGPRs();
clobberSet.exclude(RegisterSet::stackRegisters());
clobberSet.exclude(RegisterSet::reservedHardwareRegisters());
Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
Value* argument = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
root->appendNewControlValue(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, Trunc, Origin(),
condition),
FrequentedBlock(thenCase), FrequentedBlock(elseCase));
Value* truncated = thenCase->appendNew<Value>(proc, ZExt32, Origin(),
thenCase->appendNew<Value>(proc, Trunc, Origin(), argument));
UpsilonValue* thenResult = thenCase->appendNew<UpsilonValue>(proc, Origin(), truncated);
thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
UpsilonValue* elseResult = elseCase->appendNew<UpsilonValue>(proc, Origin(), argument);
elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail));
for (unsigned i = 0; i < 100; ++i) {
PatchpointValue* preventTailDuplication = tail->appendNew<PatchpointValue>(proc, Void, Origin());
preventTailDuplication->clobberLate(clobberSet);
preventTailDuplication->setGenerator([] (CCallHelpers&, const StackmapGenerationParams&) { });
}
PatchpointValue* forceSpill = tail->appendNew<PatchpointValue>(proc, Void, Origin());
forceSpill->clobberLate(clobberSet);
forceSpill->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
clobberSet.forEach([&] (Reg reg) {
jit.move(CCallHelpers::TrustedImm64(0xffffffffffffffff), reg.gpr());
});
});
Value* phi = tail->appendNew<Value>(proc, Phi, Int64, Origin());
thenResult->setPhi(phi);
elseResult->setPhi(phi);
tail->appendNewControlValue(proc, Return, Origin(), phi);
auto code = compileProc(proc);
CHECK(invoke<uint64_t>(*code, 1, 0xffffffff00000000) == 0);
CHECK(invoke<uint64_t>(*code, 0, 0xffffffff00000000) == 0xffffffff00000000);
// A second time since the previous run is still on the stack.
CHECK(invoke<uint64_t>(*code, 1, 0xffffffff00000000) == 0);
}
void testLateRegister()
{
Procedure proc;
if (!proc.optLevel()) {
// FIXME: Make O0 handle such situations:
// https://bugs.webkit.org/show_bug.cgi?id=194633
return;
}
BasicBlock* root = proc.addBlock();
// This works by making all but 1 register be input to the first patchpoint as LateRegister.
// The other 1 register is just a regular Register input. We assert our result is the regular
// register input. There would be no other way for the register allocator to arrange things
// because LateRegister interferes with the result.
// Then, the second patchpoint takes the result of the first as an argument and asks for
// it in a register that was a LateRegister. This is to incentivize the register allocator
// to use that LateRegister as the result for the first patchpoint. But of course it can not do that.
// So it must issue a mov after the first patchpoint from the first's result into the second's input.
RegisterSet regs = RegisterSet::allGPRs();
regs.exclude(RegisterSet::stackRegisters());
regs.exclude(RegisterSet::reservedHardwareRegisters());
Vector<Value*> lateUseArgs;
unsigned result = 0;
for (GPRReg reg = CCallHelpers::firstRegister(); reg <= CCallHelpers::lastRegister(); reg = CCallHelpers::nextRegister(reg)) {
if (!regs.get(reg))
continue;
result++;
if (reg == GPRInfo::regT0)
continue;
Value* value = root->appendNew<Const64Value>(proc, Origin(), 1);
lateUseArgs.append(value);
}
Value* regularUse = root->appendNew<Const64Value>(proc, Origin(), 1);
PatchpointValue* firstPatchpoint = root->appendNew<PatchpointValue>(proc, Int64, Origin());
{
unsigned i = 0;
for (GPRReg reg = CCallHelpers::firstRegister(); reg <= CCallHelpers::lastRegister(); reg = CCallHelpers::nextRegister(reg)) {
if (!regs.get(reg))
continue;
if (reg == GPRInfo::regT0)
continue;
Value* value = lateUseArgs[i++];
firstPatchpoint->append(value, ValueRep::lateReg(reg));
}
firstPatchpoint->append(regularUse, ValueRep::reg(GPRInfo::regT0));
}
firstPatchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
CHECK(params[0].gpr() == GPRInfo::regT0);
// Note that regT0 should also start off as 1, so we're implicitly starting our add with 1, which is also an argument.
unsigned skipped = 0;
for (unsigned i = 1; i < params.size(); i++) {
if (params[i].gpr() == params[0].gpr()) {
skipped = i;
continue;
}
jit.add64(params[i].gpr(), params[0].gpr());
}
CHECK(!!skipped);
});
PatchpointValue* secondPatchpoint = root->appendNew<PatchpointValue>(proc, Int64, Origin());
secondPatchpoint->append(firstPatchpoint, ValueRep::reg(GPRInfo::regT1));
secondPatchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
CHECK(params[1].gpr() == GPRInfo::regT1);
jit.nop();
jit.nop();
jit.move(params[1].gpr(), params[0].gpr());
jit.nop();
jit.nop();
});
root->appendNewControlValue(proc, Return, Origin(), secondPatchpoint);
auto code = compileProc(proc);
CHECK(invoke<uint64_t>(*code) == result);
}
void interpreterPrint(Vector<intptr_t>* stream, intptr_t value)
{
stream->append(value);
}
void testInterpreter()
{
// This implements a silly interpreter to test building custom switch statements using
// Patchpoint.
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* dispatch = proc.addBlock();
BasicBlock* addToDataPointer = proc.addBlock();
BasicBlock* addToCodePointer = proc.addBlock();
BasicBlock* addToCodePointerTaken = proc.addBlock();
BasicBlock* addToCodePointerNotTaken = proc.addBlock();
BasicBlock* addToData = proc.addBlock();
BasicBlock* print = proc.addBlock();
BasicBlock* stop = proc.addBlock();
Variable* dataPointer = proc.addVariable(pointerType());
Variable* codePointer = proc.addVariable(pointerType());
root->appendNew<VariableValue>(
proc, Set, Origin(), dataPointer,
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
root->appendNew<VariableValue>(
proc, Set, Origin(), codePointer,
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
Value* context = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2);
root->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch));
// NOTE: It's totally valid for this patchpoint to be tail-duplicated.
Value* codePointerValue =
dispatch->appendNew<VariableValue>(proc, B3::Get, Origin(), codePointer);
Value* opcode = dispatch->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(), codePointerValue);
PatchpointValue* polyJump = dispatch->appendNew<PatchpointValue>(proc, Void, Origin());
polyJump->effects = Effects();
polyJump->effects.terminal = true;
polyJump->appendSomeRegister(opcode);
polyJump->clobber(RegisterSet::macroScratchRegisters());
polyJump->numGPScratchRegisters = 2;
dispatch->appendSuccessor(FrequentedBlock(addToDataPointer));
dispatch->appendSuccessor(FrequentedBlock(addToCodePointer));
dispatch->appendSuccessor(FrequentedBlock(addToData));
dispatch->appendSuccessor(FrequentedBlock(print));
dispatch->appendSuccessor(FrequentedBlock(stop));
// Our "opcodes".
static constexpr intptr_t AddDP = 0;
static constexpr intptr_t AddCP = 1;
static constexpr intptr_t Add = 2;
static constexpr intptr_t Print = 3;
static constexpr intptr_t Stop = 4;
polyJump->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
Vector<Box<CCallHelpers::Label>> labels = params.successorLabels();
MacroAssemblerCodePtr<B3CompilationPtrTag>* jumpTable = bitwise_cast<MacroAssemblerCodePtr<B3CompilationPtrTag>*>(
params.proc().addDataSection(sizeof(MacroAssemblerCodePtr<B3CompilationPtrTag>) * labels.size()));
GPRReg scratch = params.gpScratch(0);
jit.move(CCallHelpers::TrustedImmPtr(jumpTable), scratch);
jit.load64(CCallHelpers::BaseIndex(scratch, params[0].gpr(), CCallHelpers::ScalePtr), scratch);
jit.farJump(scratch, B3CompilationPtrTag);
jit.addLinkTask(
[&, jumpTable, labels] (LinkBuffer& linkBuffer) {
for (unsigned i = labels.size(); i--;)
jumpTable[i] = linkBuffer.locationOf<B3CompilationPtrTag>(*labels[i]);
});
});
// AddDP <operand>: adds <operand> to DP.
codePointerValue =
addToDataPointer->appendNew<VariableValue>(proc, B3::Get, Origin(), codePointer);
addToDataPointer->appendNew<VariableValue>(
proc, Set, Origin(), dataPointer,
addToDataPointer->appendNew<Value>(
proc, B3::Add, Origin(),
addToDataPointer->appendNew<VariableValue>(proc, B3::Get, Origin(), dataPointer),
addToDataPointer->appendNew<Value>(
proc, Mul, Origin(),
addToDataPointer->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(), codePointerValue, static_cast<int32_t>(sizeof(intptr_t))),
addToDataPointer->appendIntConstant(
proc, Origin(), pointerType(), sizeof(intptr_t)))));
addToDataPointer->appendNew<VariableValue>(
proc, Set, Origin(), codePointer,
addToDataPointer->appendNew<Value>(
proc, B3::Add, Origin(), codePointerValue,
addToDataPointer->appendIntConstant(
proc, Origin(), pointerType(), sizeof(intptr_t) * 2)));
addToDataPointer->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch));
// AddCP <operand>: adds <operand> to CP if the current value at DP is non-zero, otherwise
// falls through normally.
codePointerValue =
addToCodePointer->appendNew<VariableValue>(proc, B3::Get, Origin(), codePointer);
Value* dataPointerValue =
addToCodePointer->appendNew<VariableValue>(proc, B3::Get, Origin(), dataPointer);
addToCodePointer->appendNewControlValue(
proc, Branch, Origin(),
addToCodePointer->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(), dataPointerValue),
FrequentedBlock(addToCodePointerTaken), FrequentedBlock(addToCodePointerNotTaken));
addToCodePointerTaken->appendNew<VariableValue>(
proc, Set, Origin(), codePointer,
addToCodePointerTaken->appendNew<Value>(
proc, B3::Add, Origin(), codePointerValue,
addToCodePointerTaken->appendNew<Value>(
proc, Mul, Origin(),
addToCodePointerTaken->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(), codePointerValue, static_cast<int32_t>(sizeof(intptr_t))),
addToCodePointerTaken->appendIntConstant(
proc, Origin(), pointerType(), sizeof(intptr_t)))));
addToCodePointerTaken->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch));
addToCodePointerNotTaken->appendNew<VariableValue>(
proc, Set, Origin(), codePointer,
addToCodePointerNotTaken->appendNew<Value>(
proc, B3::Add, Origin(), codePointerValue,
addToCodePointerNotTaken->appendIntConstant(
proc, Origin(), pointerType(), sizeof(intptr_t) * 2)));
addToCodePointerNotTaken->appendNewControlValue(
proc, Jump, Origin(), FrequentedBlock(dispatch));
// Add <operand>: adds <operand> to the slot pointed to by DP.
codePointerValue = addToData->appendNew<VariableValue>(proc, B3::Get, Origin(), codePointer);
dataPointerValue = addToData->appendNew<VariableValue>(proc, B3::Get, Origin(), dataPointer);
addToData->appendNew<MemoryValue>(
proc, Store, Origin(),
addToData->appendNew<Value>(
proc, B3::Add, Origin(),
addToData->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(), dataPointerValue),
addToData->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(), codePointerValue, static_cast<int32_t>(sizeof(intptr_t)))),
dataPointerValue);
addToData->appendNew<VariableValue>(
proc, Set, Origin(), codePointer,
addToData->appendNew<Value>(
proc, B3::Add, Origin(), codePointerValue,
addToData->appendIntConstant(proc, Origin(), pointerType(), sizeof(intptr_t) * 2)));
addToData->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch));
// Print: "prints" the value pointed to by DP. What this actually means is that the value is
// appended to the stream vector by the interpreterPrint function.
codePointerValue = print->appendNew<VariableValue>(proc, B3::Get, Origin(), codePointer);
dataPointerValue = print->appendNew<VariableValue>(proc, B3::Get, Origin(), dataPointer);
print->appendNew<CCallValue>(
proc, Void, Origin(),
print->appendNew<ConstPtrValue>(
proc, Origin(), tagCFunction<B3CCallPtrTag>(interpreterPrint)),
context,
print->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), dataPointerValue));
print->appendNew<VariableValue>(
proc, Set, Origin(), codePointer,
print->appendNew<Value>(
proc, B3::Add, Origin(), codePointerValue,
print->appendIntConstant(proc, Origin(), pointerType(), sizeof(intptr_t))));
print->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch));
// Stop: returns.
stop->appendNewControlValue(
proc, Return, Origin(),
stop->appendIntConstant(proc, Origin(), pointerType(), 0));
auto interpreter = compileProc(proc);
Vector<uintptr_t> data;
Vector<uintptr_t> code;
Vector<uintptr_t> stream;
data.append(1);
data.append(0);
if (shouldBeVerbose())
dataLog("data = ", listDump(data), "\n");
// We'll write a program that prints the numbers 1..100.
// We expect DP to point at #0.
code.append(AddCP);
code.append(6); // go to loop body
// Loop re-entry:
// We expect DP to point at #1 and for #1 to be offset by -100.
code.append(Add);
code.append(100);
code.append(AddDP);
code.append(-1);
// Loop header:
// We expect DP to point at #0.
code.append(AddDP);
code.append(1);
code.append(Add);
code.append(1);
code.append(Print);
code.append(Add);
code.append(-100);
// We want to stop if it's zero and continue if it's non-zero. AddCP takes the branch if it's
// non-zero.
code.append(AddCP);
code.append(-11); // go to loop re-entry.
code.append(Stop);
if (shouldBeVerbose())
dataLog("code = ", listDump(code), "\n");
CHECK(!invoke<intptr_t>(*interpreter, data.data(), code.data(), &stream));
CHECK(stream.size() == 100);
for (unsigned i = 0; i < 100; ++i)
CHECK(stream[i] == i + 1);
if (shouldBeVerbose())
dataLog("stream = ", listDump(stream), "\n");
}
void testReduceStrengthCheckBottomUseInAnotherBlock()
{
Procedure proc;
if (proc.optLevel() < 1)
return;
BasicBlock* one = proc.addBlock();
BasicBlock* two = proc.addBlock();
CheckValue* check = one->appendNew<CheckValue>(
proc, Check, Origin(), one->appendNew<Const32Value>(proc, Origin(), 1));
check->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
jit.move(CCallHelpers::TrustedImm32(666), GPRInfo::returnValueGPR);
jit.emitFunctionEpilogue();
jit.ret();
});
Value* arg = one->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
one->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(two));
check = two->appendNew<CheckValue>(
proc, CheckAdd, Origin(), arg,
two->appendNew<ConstPtrValue>(proc, Origin(), 1));
check->setGenerator(
[&] (CCallHelpers&, const StackmapGenerationParams&) {
CHECK(!"Should not execute");
});
two->appendNewControlValue(proc, Return, Origin(), check);
proc.resetReachability();
reduceStrength(proc);
}
void testResetReachabilityDanglingReference()
{
Procedure proc;
BasicBlock* one = proc.addBlock();
BasicBlock* two = proc.addBlock();
UpsilonValue* upsilon = one->appendNew<UpsilonValue>(
proc, Origin(), one->appendNew<Const32Value>(proc, Origin(), 42));
one->appendNewControlValue(proc, Oops, Origin());
Value* phi = two->appendNew<Value>(proc, Phi, Int32, Origin());
upsilon->setPhi(phi);
two->appendNewControlValue(proc, Oops, Origin());
proc.resetReachability();
validate(proc);
}
void testEntrySwitchSimple()
{
Procedure proc;
proc.setNumEntrypoints(3);
BasicBlock* root = proc.addBlock();
BasicBlock* one = proc.addBlock();
BasicBlock* two = proc.addBlock();
BasicBlock* three = proc.addBlock();
root->appendNew<Value>(proc, EntrySwitch, Origin());
root->appendSuccessor(FrequentedBlock(one));
root->appendSuccessor(FrequentedBlock(two));
root->appendSuccessor(FrequentedBlock(three));
one->appendNew<Value>(
proc, Return, Origin(),
one->appendNew<Value>(
proc, Add, Origin(),
one->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
one->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
two->appendNew<Value>(
proc, Return, Origin(),
two->appendNew<Value>(
proc, Sub, Origin(),
two->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
two->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
three->appendNew<Value>(
proc, Return, Origin(),
three->appendNew<Value>(
proc, Mul, Origin(),
three->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
three->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
prepareForGeneration(proc);
CCallHelpers jit;
generate(proc, jit);
LinkBuffer linkBuffer(jit, nullptr);
CodeLocationLabel<B3CompilationPtrTag> labelOne = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(0));
CodeLocationLabel<B3CompilationPtrTag> labelTwo = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(1));
CodeLocationLabel<B3CompilationPtrTag> labelThree = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(2));
MacroAssemblerCodeRef<B3CompilationPtrTag> codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation");
CHECK(invoke<int>(labelOne, 1, 2) == 3);
CHECK(invoke<int>(labelTwo, 1, 2) == -1);
CHECK(invoke<int>(labelThree, 1, 2) == 2);
CHECK(invoke<int>(labelOne, -1, 2) == 1);
CHECK(invoke<int>(labelTwo, -1, 2) == -3);
CHECK(invoke<int>(labelThree, -1, 2) == -2);
}
void testEntrySwitchNoEntrySwitch()
{
Procedure proc;
proc.setNumEntrypoints(3);
BasicBlock* root = proc.addBlock();
root->appendNew<Value>(
proc, Return, Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1)));
prepareForGeneration(proc);
CCallHelpers jit;
generate(proc, jit);
LinkBuffer linkBuffer(jit, nullptr);
CodeLocationLabel<B3CompilationPtrTag> labelOne = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(0));
CodeLocationLabel<B3CompilationPtrTag> labelTwo = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(1));
CodeLocationLabel<B3CompilationPtrTag> labelThree = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(2));
MacroAssemblerCodeRef<B3CompilationPtrTag> codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation");
CHECK_EQ(invoke<int>(labelOne, 1, 2), 3);
CHECK_EQ(invoke<int>(labelTwo, 1, 2), 3);
CHECK_EQ(invoke<int>(labelThree, 1, 2), 3);
CHECK_EQ(invoke<int>(labelOne, -1, 2), 1);
CHECK_EQ(invoke<int>(labelTwo, -1, 2), 1);
CHECK_EQ(invoke<int>(labelThree, -1, 2), 1);
}
void testEntrySwitchWithCommonPaths()
{
Procedure proc;
proc.setNumEntrypoints(3);
BasicBlock* root = proc.addBlock();
BasicBlock* one = proc.addBlock();
BasicBlock* two = proc.addBlock();
BasicBlock* three = proc.addBlock();
BasicBlock* end = proc.addBlock();
root->appendNew<Value>(proc, EntrySwitch, Origin());
root->appendSuccessor(FrequentedBlock(one));
root->appendSuccessor(FrequentedBlock(two));
root->appendSuccessor(FrequentedBlock(three));
UpsilonValue* upsilonOne = one->appendNew<UpsilonValue>(
proc, Origin(),
one->appendNew<Value>(
proc, Add, Origin(),
one->appendNew<Value>(
proc, Trunc, Origin(),
one->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
one->appendNew<Value>(
proc, Trunc, Origin(),
one->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
one->appendNew<Value>(proc, Jump, Origin());
one->setSuccessors(FrequentedBlock(end));
UpsilonValue* upsilonTwo = two->appendNew<UpsilonValue>(
proc, Origin(),
two->appendNew<Value>(
proc, Sub, Origin(),
two->appendNew<Value>(
proc, Trunc, Origin(),
two->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
two->appendNew<Value>(
proc, Trunc, Origin(),
two->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
two->appendNew<Value>(proc, Jump, Origin());
two->setSuccessors(FrequentedBlock(end));
UpsilonValue* upsilonThree = three->appendNew<UpsilonValue>(
proc, Origin(),
three->appendNew<Value>(
proc, Mul, Origin(),
three->appendNew<Value>(
proc, Trunc, Origin(),
three->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)),
three->appendNew<Value>(
proc, Trunc, Origin(),
three->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
three->appendNew<Value>(proc, Jump, Origin());
three->setSuccessors(FrequentedBlock(end));
Value* phi = end->appendNew<Value>(proc, Phi, Int32, Origin());
upsilonOne->setPhi(phi);
upsilonTwo->setPhi(phi);
upsilonThree->setPhi(phi);
end->appendNew<Value>(
proc, Return, Origin(),
end->appendNew<Value>(
proc, chill(Mod), Origin(),
phi, end->appendNew<Value>(
proc, Trunc, Origin(),
end->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2))));
prepareForGeneration(proc);
CCallHelpers jit;
generate(proc, jit);
LinkBuffer linkBuffer(jit, nullptr);
CodeLocationLabel<B3CompilationPtrTag> labelOne = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(0));
CodeLocationLabel<B3CompilationPtrTag> labelTwo = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(1));
CodeLocationLabel<B3CompilationPtrTag> labelThree = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(2));
MacroAssemblerCodeRef<B3CompilationPtrTag> codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation");
CHECK_EQ(invoke<int>(labelOne, 1, 2, 10), 3);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 10), -1);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 10), 2);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 10), 1);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 10), -3);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 10), -2);
CHECK_EQ(invoke<int>(labelOne, 1, 2, 2), 1);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 2), -1);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 2), 0);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 2), 1);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 2), -1);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 2), 0);
CHECK_EQ(invoke<int>(labelOne, 1, 2, 0), 0);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 0), 0);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 0), 0);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 0), 0);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 0), 0);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 0), 0);
}
void testEntrySwitchWithCommonPathsAndNonTrivialEntrypoint()
{
Procedure proc;
proc.setNumEntrypoints(3);
BasicBlock* root = proc.addBlock();
BasicBlock* negate = proc.addBlock();
BasicBlock* dispatch = proc.addBlock();
BasicBlock* one = proc.addBlock();
BasicBlock* two = proc.addBlock();
BasicBlock* three = proc.addBlock();
BasicBlock* end = proc.addBlock();
UpsilonValue* upsilonBase = root->appendNew<UpsilonValue>(
proc, Origin(), root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR3),
root->appendNew<ConstPtrValue>(proc, Origin(), 0xff)));
root->setSuccessors(FrequentedBlock(negate), FrequentedBlock(dispatch));
UpsilonValue* upsilonNegate = negate->appendNew<UpsilonValue>(
proc, Origin(),
negate->appendNew<Value>(
proc, Neg, Origin(),
negate->appendNew<Value>(
proc, Trunc, Origin(),
negate->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0))));
negate->appendNew<Value>(proc, Jump, Origin());
negate->setSuccessors(FrequentedBlock(dispatch));
Value* arg0 = dispatch->appendNew<Value>(proc, Phi, Int32, Origin());
upsilonBase->setPhi(arg0);
upsilonNegate->setPhi(arg0);
dispatch->appendNew<Value>(proc, EntrySwitch, Origin());
dispatch->appendSuccessor(FrequentedBlock(one));
dispatch->appendSuccessor(FrequentedBlock(two));
dispatch->appendSuccessor(FrequentedBlock(three));
UpsilonValue* upsilonOne = one->appendNew<UpsilonValue>(
proc, Origin(),
one->appendNew<Value>(
proc, Add, Origin(),
arg0, one->appendNew<Value>(
proc, Trunc, Origin(),
one->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
one->appendNew<Value>(proc, Jump, Origin());
one->setSuccessors(FrequentedBlock(end));
UpsilonValue* upsilonTwo = two->appendNew<UpsilonValue>(
proc, Origin(),
two->appendNew<Value>(
proc, Sub, Origin(),
arg0, two->appendNew<Value>(
proc, Trunc, Origin(),
two->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
two->appendNew<Value>(proc, Jump, Origin());
two->setSuccessors(FrequentedBlock(end));
UpsilonValue* upsilonThree = three->appendNew<UpsilonValue>(
proc, Origin(),
three->appendNew<Value>(
proc, Mul, Origin(),
arg0, three->appendNew<Value>(
proc, Trunc, Origin(),
three->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1))));
three->appendNew<Value>(proc, Jump, Origin());
three->setSuccessors(FrequentedBlock(end));
Value* phi = end->appendNew<Value>(proc, Phi, Int32, Origin());
upsilonOne->setPhi(phi);
upsilonTwo->setPhi(phi);
upsilonThree->setPhi(phi);
end->appendNew<Value>(
proc, Return, Origin(),
end->appendNew<Value>(
proc, chill(Mod), Origin(),
phi, end->appendNew<Value>(
proc, Trunc, Origin(),
end->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR2))));
prepareForGeneration(proc);
CCallHelpers jit;
generate(proc, jit);
LinkBuffer linkBuffer(jit, nullptr);
CodeLocationLabel<B3CompilationPtrTag> labelOne = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(0));
CodeLocationLabel<B3CompilationPtrTag> labelTwo = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(1));
CodeLocationLabel<B3CompilationPtrTag> labelThree = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(2));
MacroAssemblerCodeRef<B3CompilationPtrTag> codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation");
CHECK_EQ(invoke<int>(labelOne, 1, 2, 10, false), 3);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 10, false), -1);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 10, false), 2);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 10, false), 1);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 10, false), -3);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 10, false), -2);
CHECK_EQ(invoke<int>(labelOne, 1, 2, 10, true), 1);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 10, true), -3);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 10, true), -2);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 10, true), 3);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 10, true), -1);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 10, true), 2);
CHECK_EQ(invoke<int>(labelOne, 1, 2, 2, false), 1);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 2, false), -1);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 2, false), 0);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 2, false), 1);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 2, false), -1);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 2, false), 0);
CHECK_EQ(invoke<int>(labelOne, 1, 2, 0, false), 0);
CHECK_EQ(invoke<int>(labelTwo, 1, 2, 0, false), 0);
CHECK_EQ(invoke<int>(labelThree, 1, 2, 0, false), 0);
CHECK_EQ(invoke<int>(labelOne, -1, 2, 0, false), 0);
CHECK_EQ(invoke<int>(labelTwo, -1, 2, 0, false), 0);
CHECK_EQ(invoke<int>(labelThree, -1, 2, 0, false), 0);
}
void testEntrySwitchLoop()
{
// This is a completely absurd use of EntrySwitch, where it impacts the loop condition. This
// should cause duplication of either nearly the entire Procedure. At time of writing, we ended
// up duplicating all of it, which is fine. It's important to test this case, to make sure that
// the duplication algorithm can handle interesting control flow.
Procedure proc;
proc.setNumEntrypoints(2);
BasicBlock* root = proc.addBlock();
BasicBlock* loopHeader = proc.addBlock();
BasicBlock* loopFooter = proc.addBlock();
BasicBlock* end = proc.addBlock();
UpsilonValue* initialValue = root->appendNew<UpsilonValue>(
proc, Origin(), root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0)));
root->appendNew<Value>(proc, Jump, Origin());
root->setSuccessors(loopHeader);
Value* valueInLoop = loopHeader->appendNew<Value>(proc, Phi, Int32, Origin());
initialValue->setPhi(valueInLoop);
Value* newValue = loopHeader->appendNew<Value>(
proc, Add, Origin(), valueInLoop,
loopHeader->appendNew<Const32Value>(proc, Origin(), 1));
loopHeader->appendNew<Value>(proc, EntrySwitch, Origin());
loopHeader->appendSuccessor(end);
loopHeader->appendSuccessor(loopFooter);
loopFooter->appendNew<UpsilonValue>(proc, Origin(), newValue, valueInLoop);
loopFooter->appendNew<Value>(
proc, Branch, Origin(),
loopFooter->appendNew<Value>(
proc, LessThan, Origin(), newValue,
loopFooter->appendNew<Const32Value>(proc, Origin(), 100)));
loopFooter->setSuccessors(loopHeader, end);
end->appendNew<Value>(proc, Return, Origin(), newValue);
prepareForGeneration(proc);
CCallHelpers jit;
generate(proc, jit);
LinkBuffer linkBuffer(jit, nullptr);
CodeLocationLabel<B3CompilationPtrTag> labelOne = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(0));
CodeLocationLabel<B3CompilationPtrTag> labelTwo = linkBuffer.locationOf<B3CompilationPtrTag>(proc.code().entrypointLabel(1));
MacroAssemblerCodeRef<B3CompilationPtrTag> codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation");
CHECK(invoke<int>(labelOne, 0) == 1);
CHECK(invoke<int>(labelOne, 42) == 43);
CHECK(invoke<int>(labelOne, 1000) == 1001);
CHECK(invoke<int>(labelTwo, 0) == 100);
CHECK(invoke<int>(labelTwo, 42) == 100);
CHECK(invoke<int>(labelTwo, 1000) == 1001);
}
void testSomeEarlyRegister()
{
auto run = [&] (bool succeed) {
Procedure proc;
BasicBlock* root = proc.addBlock();
PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
patchpoint->resultConstraints = { ValueRep::reg(GPRInfo::returnValueGPR) };
bool ranFirstPatchpoint = false;
patchpoint->setGenerator(
[&] (CCallHelpers&, const StackmapGenerationParams& params) {
CHECK(params[0].gpr() == GPRInfo::returnValueGPR);
ranFirstPatchpoint = true;
});
Value* arg = patchpoint;
patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
patchpoint->appendSomeRegister(arg);
if (succeed)
patchpoint->resultConstraints = { ValueRep::SomeEarlyRegister };
bool ranSecondPatchpoint = false;
unsigned optLevel = proc.optLevel();
patchpoint->setGenerator(
[&] (CCallHelpers&, const StackmapGenerationParams& params) {
if (succeed)
CHECK(params[0].gpr() != params[1].gpr());
else if (optLevel > 1)
CHECK(params[0].gpr() == params[1].gpr());
ranSecondPatchpoint = true;
});
root->appendNew<Value>(proc, Return, Origin(), patchpoint);
compileProc(proc);
CHECK(ranFirstPatchpoint);
CHECK(ranSecondPatchpoint);
};
run(true);
run(false);
}
void testBranchBitAndImmFusion(
B3::Opcode valueModifier, Type valueType, int64_t constant,
Air::Opcode expectedOpcode, Air::Arg::Kind firstKind)
{
// Currently this test should pass on all CPUs. But some CPUs may not support this fused
// instruction. It's OK to skip this test on those CPUs.
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* one = proc.addBlock();
BasicBlock* two = proc.addBlock();
Value* left = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
if (valueModifier != Identity) {
if (MemoryValue::accepts(valueModifier))
left = root->appendNew<MemoryValue>(proc, valueModifier, valueType, Origin(), left);
else
left = root->appendNew<Value>(proc, valueModifier, valueType, Origin(), left);
}
root->appendNew<Value>(
proc, Branch, Origin(),
root->appendNew<Value>(
proc, BitAnd, Origin(), left,
root->appendIntConstant(proc, Origin(), valueType, constant)));
root->setSuccessors(FrequentedBlock(one), FrequentedBlock(two));
one->appendNew<Value>(proc, Oops, Origin());
two->appendNew<Value>(proc, Oops, Origin());
lowerToAirForTesting(proc);
// The first basic block must end in a BranchTest64(resCond, tmp, bitImm).
Air::Inst terminal = proc.code()[0]->last();
CHECK_EQ(terminal.kind.opcode, expectedOpcode);
CHECK_EQ(terminal.args[0].kind(), Air::Arg::ResCond);
CHECK_EQ(terminal.args[1].kind(), firstKind);
CHECK(terminal.args[2].kind() == Air::Arg::BitImm || terminal.args[2].kind() == Air::Arg::BitImm64);
}
void testTerminalPatchpointThatNeedsToBeSpilled()
{
// This is a unit test for how FTL's heap allocation fast paths behave.
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* success = proc.addBlock();
BasicBlock* slowPath = proc.addBlock();
PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
patchpoint->effects.terminal = true;
patchpoint->clobber(RegisterSet::macroScratchRegisters());
root->appendSuccessor(success);
root->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
patchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
jit.move(CCallHelpers::TrustedImm32(42), params[0].gpr());
CCallHelpers::Jump jumpToSuccess;
if (!params.fallsThroughToSuccessor(0))
jumpToSuccess = jit.jump();
Vector<Box<CCallHelpers::Label>> labels = params.successorLabels();
params.addLatePath(
[=] (CCallHelpers& jit) {
if (jumpToSuccess.isSet())
jumpToSuccess.linkTo(*labels[0], &jit);
});
});
Vector<Value*> args;
{
RegisterSet fillAllGPRsSet = proc.mutableGPRs();
for (unsigned i = 0; i < fillAllGPRsSet.numberOfSetRegisters(); i++)
args.append(success->appendNew<Const32Value>(proc, Origin(), i));
}
{
// Now force all values into every available register.
PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
for (Value* v : args)
p->append(v, ValueRep::SomeRegister);
p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
}
{
// Now require the original patchpoint to be materialized into a register.
PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
p->append(patchpoint, ValueRep::SomeRegister);
p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
}
success->appendNew<Value>(proc, Return, Origin(), success->appendNew<Const32Value>(proc, Origin(), 10));
slowPath->appendNew<Value>(proc, Return, Origin(), slowPath->appendNew<Const32Value>(proc, Origin(), 20));
auto code = compileProc(proc);
CHECK_EQ(invoke<int>(*code), 10);
}
void testTerminalPatchpointThatNeedsToBeSpilled2()
{
// This is a unit test for how FTL's heap allocation fast paths behave.
Procedure proc;
// FIXME: Air O0/O1 allocator can't handle such programs. We rely on WasmAirIRGenerator
// to not use any such constructs where the register allocator is cornered in such
// a way.
// https://bugs.webkit.org/show_bug.cgi?id=194633
if (proc.optLevel() < 2)
return;
BasicBlock* root = proc.addBlock();
BasicBlock* one = proc.addBlock();
BasicBlock* success = proc.addBlock();
BasicBlock* slowPath = proc.addBlock();
Value* arg = root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
root->appendNew<Value>(
proc, Branch, Origin(), arg);
root->appendSuccessor(one);
root->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
PatchpointValue* patchpoint = one->appendNew<PatchpointValue>(proc, Int32, Origin());
patchpoint->effects.terminal = true;
patchpoint->clobber(RegisterSet::macroScratchRegisters());
patchpoint->append(arg, ValueRep::SomeRegister);
one->appendSuccessor(success);
one->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
patchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
jit.move(CCallHelpers::TrustedImm32(666), params[0].gpr());
auto goToFastPath = jit.branch32(CCallHelpers::Equal, params[1].gpr(), CCallHelpers::TrustedImm32(42));
auto jumpToSlow = jit.jump();
// Make sure the asserts here pass.
params.fallsThroughToSuccessor(0);
params.fallsThroughToSuccessor(1);
Vector<Box<CCallHelpers::Label>> labels = params.successorLabels();
params.addLatePath(
[=] (CCallHelpers& jit) {
goToFastPath.linkTo(*labels[0], &jit);
jumpToSlow.linkTo(*labels[1], &jit);
});
});
Vector<Value*> args;
{
RegisterSet fillAllGPRsSet = proc.mutableGPRs();
for (unsigned i = 0; i < fillAllGPRsSet.numberOfSetRegisters(); i++)
args.append(success->appendNew<Const32Value>(proc, Origin(), i));
}
{
// Now force all values into every available register.
PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
for (Value* v : args)
p->append(v, ValueRep::SomeRegister);
p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
}
{
// Now require the original patchpoint to be materialized into a register.
PatchpointValue* p = success->appendNew<PatchpointValue>(proc, Void, Origin());
p->append(patchpoint, ValueRep::SomeRegister);
p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { });
}
success->appendNew<Value>(proc, Return, Origin(), patchpoint);
slowPath->appendNew<Value>(proc, Return, Origin(), arg);
auto original1 = Options::maxB3TailDupBlockSize();
auto original2 = Options::maxB3TailDupBlockSuccessors();
// Tail duplication will break the critical edge we're trying to test because it
// will clone the slowPath block for both edges to it!
Options::maxB3TailDupBlockSize() = 0;
Options::maxB3TailDupBlockSuccessors() = 0;
auto code = compileProc(proc);
CHECK_EQ(invoke<int>(*code, 1), 1);
CHECK_EQ(invoke<int>(*code, 0), 0);
CHECK_EQ(invoke<int>(*code, 42), 666);
Options::maxB3TailDupBlockSize() = original1;
Options::maxB3TailDupBlockSuccessors() = original2;
}
void testPatchpointTerminalReturnValue(bool successIsRare)
{
// This is a unit test for how FTL's heap allocation fast paths behave.
Procedure proc;
BasicBlock* root = proc.addBlock();
BasicBlock* success = proc.addBlock();
BasicBlock* slowPath = proc.addBlock();
BasicBlock* continuation = proc.addBlock();
Value* arg = root->appendNew<Value>(
proc, Trunc, Origin(),
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
patchpoint->effects.terminal = true;
patchpoint->clobber(RegisterSet::macroScratchRegisters());
if (successIsRare) {
root->appendSuccessor(FrequentedBlock(success, FrequencyClass::Rare));
root->appendSuccessor(slowPath);
} else {
root->appendSuccessor(success);
root->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare));
}
patchpoint->appendSomeRegister(arg);
patchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
CCallHelpers::Jump jumpToSlow =
jit.branch32(CCallHelpers::Above, params[1].gpr(), CCallHelpers::TrustedImm32(42));
jit.add32(CCallHelpers::TrustedImm32(31), params[1].gpr(), params[0].gpr());
CCallHelpers::Jump jumpToSuccess;
if (!params.fallsThroughToSuccessor(0))
jumpToSuccess = jit.jump();
Vector<Box<CCallHelpers::Label>> labels = params.successorLabels();
params.addLatePath(
[=] (CCallHelpers& jit) {
jumpToSlow.linkTo(*labels[1], &jit);
if (jumpToSuccess.isSet())
jumpToSuccess.linkTo(*labels[0], &jit);
});
});
UpsilonValue* successUpsilon = success->appendNew<UpsilonValue>(proc, Origin(), patchpoint);
success->appendNew<Value>(proc, Jump, Origin());
success->setSuccessors(continuation);
UpsilonValue* slowPathUpsilon = slowPath->appendNew<UpsilonValue>(
proc, Origin(), slowPath->appendNew<Const32Value>(proc, Origin(), 666));
slowPath->appendNew<Value>(proc, Jump, Origin());
slowPath->setSuccessors(continuation);
Value* phi = continuation->appendNew<Value>(proc, Phi, Int32, Origin());
successUpsilon->setPhi(phi);
slowPathUpsilon->setPhi(phi);
continuation->appendNew<Value>(proc, Return, Origin(), phi);
auto code = compileProc(proc);
CHECK_EQ(invoke<int>(*code, 0), 31);
CHECK_EQ(invoke<int>(*code, 1), 32);
CHECK_EQ(invoke<int>(*code, 41), 72);
CHECK_EQ(invoke<int>(*code, 42), 73);
CHECK_EQ(invoke<int>(*code, 43), 666);
CHECK_EQ(invoke<int>(*code, -1), 666);
}
void testMemoryFence()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<FenceValue>(proc, Origin());
root->appendNew<Value>(proc, Return, Origin(), root->appendIntConstant(proc, Origin(), Int32, 42));
auto code = compileProc(proc);
CHECK_EQ(invoke<int>(*code), 42);
if (isX86())
checkUsesInstruction(*code, "lock or $0x0, (%rsp)");
if (isARM64())
checkUsesInstruction(*code, "dmb ish");
checkDoesNotUseInstruction(*code, "mfence");
checkDoesNotUseInstruction(*code, "dmb ishst");
}
void testStoreFence()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<FenceValue>(proc, Origin(), HeapRange::top(), HeapRange());
root->appendNew<Value>(proc, Return, Origin(), root->appendIntConstant(proc, Origin(), Int32, 42));
auto code = compileProc(proc);
CHECK_EQ(invoke<int>(*code), 42);
checkDoesNotUseInstruction(*code, "lock");
checkDoesNotUseInstruction(*code, "mfence");
if (isARM64())
checkUsesInstruction(*code, "dmb ishst");
}
void testLoadFence()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<FenceValue>(proc, Origin(), HeapRange(), HeapRange::top());
root->appendNew<Value>(proc, Return, Origin(), root->appendIntConstant(proc, Origin(), Int32, 42));
auto code = compileProc(proc);
CHECK_EQ(invoke<int>(*code), 42);
checkDoesNotUseInstruction(*code, "lock");
checkDoesNotUseInstruction(*code, "mfence");
if (isARM64())
checkUsesInstruction(*code, "dmb ish");
checkDoesNotUseInstruction(*code, "dmb ishst");
}
void testTrappingLoad()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int x = 42;
MemoryValue* value = root->appendNew<MemoryValue>(
proc, trapping(Load), Int32, Origin(),
root->appendNew<ConstPtrValue>(proc, Origin(), &x));
Effects expectedEffects;
expectedEffects.exitsSideways = true;
expectedEffects.controlDependent= true;
expectedEffects.reads = HeapRange::top();
CHECK_EQ(value->range(), HeapRange::top());
CHECK_EQ(value->effects(), expectedEffects);
value->setRange(HeapRange(0));
CHECK_EQ(value->range(), HeapRange(0));
CHECK_EQ(value->effects(), expectedEffects); // We still reads top!
root->appendNew<Value>(proc, Return, Origin(), value);
CHECK_EQ(compileAndRun<int>(proc), 42);
unsigned trapsCount = 0;
for (Air::BasicBlock* block : proc.code()) {
for (Air::Inst& inst : *block) {
if (inst.kind.effects)
trapsCount++;
}
}
CHECK_EQ(trapsCount, 1u);
}
void testTrappingStore()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int x = 42;
MemoryValue* value = root->appendNew<MemoryValue>(
proc, trapping(Store), Origin(),
root->appendNew<Const32Value>(proc, Origin(), 111),
root->appendNew<ConstPtrValue>(proc, Origin(), &x), 0);
Effects expectedEffects;
expectedEffects.exitsSideways = true;
expectedEffects.controlDependent= true;
expectedEffects.reads = HeapRange::top();
expectedEffects.writes = HeapRange::top();
CHECK_EQ(value->range(), HeapRange::top());
CHECK_EQ(value->effects(), expectedEffects);
value->setRange(HeapRange(0));
CHECK_EQ(value->range(), HeapRange(0));
expectedEffects.writes = HeapRange(0);
CHECK_EQ(value->effects(), expectedEffects); // We still reads top!
root->appendNew<Value>(proc, Return, Origin());
compileAndRun<int>(proc);
CHECK_EQ(x, 111);
unsigned trapsCount = 0;
for (Air::BasicBlock* block : proc.code()) {
for (Air::Inst& inst : *block) {
if (inst.kind.effects)
trapsCount++;
}
}
CHECK_EQ(trapsCount, 1u);
}
void testTrappingLoadAddStore()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int x = 42;
ConstPtrValue* ptr = root->appendNew<ConstPtrValue>(proc, Origin(), &x);
root->appendNew<MemoryValue>(
proc, trapping(Store), Origin(),
root->appendNew<Value>(
proc, Add, Origin(),
root->appendNew<MemoryValue>(proc, trapping(Load), Int32, Origin(), ptr),
root->appendNew<Const32Value>(proc, Origin(), 3)),
ptr, 0);
root->appendNew<Value>(proc, Return, Origin());
compileAndRun<int>(proc);
CHECK_EQ(x, 45);
bool traps = false;
for (Air::BasicBlock* block : proc.code()) {
for (Air::Inst& inst : *block) {
if (inst.kind.effects)
traps = true;
}
}
CHECK(traps);
}
void testTrappingLoadDCE()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int x = 42;
root->appendNew<MemoryValue>(
proc, trapping(Load), Int32, Origin(),
root->appendNew<ConstPtrValue>(proc, Origin(), &x));
root->appendNew<Value>(proc, Return, Origin());
compileAndRun<int>(proc);
unsigned trapsCount = 0;
for (Air::BasicBlock* block : proc.code()) {
for (Air::Inst& inst : *block) {
if (inst.kind.effects)
trapsCount++;
}
}
CHECK_EQ(trapsCount, 1u);
}
void testTrappingStoreElimination()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
int x = 42;
Value* ptr = root->appendNew<ConstPtrValue>(proc, Origin(), &x);
root->appendNew<MemoryValue>(
proc, trapping(Store), Origin(),
root->appendNew<Const32Value>(proc, Origin(), 43),
ptr);
root->appendNew<MemoryValue>(
proc, trapping(Store), Origin(),
root->appendNew<Const32Value>(proc, Origin(), 44),
ptr);
root->appendNew<Value>(proc, Return, Origin());
compileAndRun<int>(proc);
unsigned storeCount = 0;
for (Value* value : proc.values()) {
if (isStore(value->opcode()))
storeCount++;
}
CHECK_EQ(storeCount, 2u);
}
void testMoveConstants()
{
auto check = [] (Procedure& proc) {
proc.resetReachability();
if (shouldBeVerbose()) {
dataLog("IR before:\n");
dataLog(proc);
}
moveConstants(proc);
if (shouldBeVerbose()) {
dataLog("IR after:\n");
dataLog(proc);
}
UseCounts useCounts(proc);
unsigned count = 0;
for (Value* value : proc.values()) {
if (useCounts.numUses(value) && value->hasInt64())
count++;
}
if (count == 1)
return;
crashLock.lock();
dataLog("Fail in testMoveConstants: got more than one Const64:\n");
dataLog(proc);
CRASH();
};
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* a = root->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(),
root->appendNew<ConstPtrValue>(proc, Origin(), 0x123412341234));
Value* b = root->appendNew<MemoryValue>(
proc, Load, pointerType(), Origin(),
root->appendNew<ConstPtrValue>(proc, Origin(), 0x123412341334));
root->appendNew<CCallValue>(proc, Void, Origin(), a, b);
root->appendNew<Value>(proc, Return, Origin());
check(proc);
}
{
Procedure proc;
BasicBlock* root = proc.addBlock();
Value* x = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
Value* a = root->appendNew<Value>(
proc, Add, Origin(), x, root->appendNew<ConstPtrValue>(proc, Origin(), 0x123412341234));
Value* b = root->appendNew<Value>(
proc, Add, Origin(), x, root->appendNew<ConstPtrValue>(proc, Origin(), -0x123412341234));
root->appendNew<CCallValue>(proc, Void, Origin(), a, b);
root->appendNew<Value>(proc, Return, Origin());
check(proc);
}
}
void testPCOriginMapDoesntInsertNops()
{
Procedure proc;
BasicBlock* root = proc.addBlock();
CCallHelpers::Label watchpointLabel;
PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin());
patchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
watchpointLabel = jit.watchpointLabel();
});
patchpoint = root->appendNew<PatchpointValue>(proc, Void, Origin());
patchpoint->setGenerator(
[&] (CCallHelpers& jit, const StackmapGenerationParams&) {
CCallHelpers::Label labelIgnoringWatchpoints = jit.labelIgnoringWatchpoints();
CHECK(watchpointLabel == labelIgnoringWatchpoints);
});
root->appendNew<Value>(proc, Return, Origin());
compileProc(proc);
}
void addSShrShTests(const char* filter, Deque<RefPtr<SharedTask<void()>>>& tasks)
{
RUN(testSShrShl32(42, 24, 24));
RUN(testSShrShl32(-42, 24, 24));
RUN(testSShrShl32(4200, 24, 24));
RUN(testSShrShl32(-4200, 24, 24));
RUN(testSShrShl32(4200000, 24, 24));
RUN(testSShrShl32(-4200000, 24, 24));
RUN(testSShrShl32(42, 16, 16));
RUN(testSShrShl32(-42, 16, 16));
RUN(testSShrShl32(4200, 16, 16));
RUN(testSShrShl32(-4200, 16, 16));
RUN(testSShrShl32(4200000, 16, 16));
RUN(testSShrShl32(-4200000, 16, 16));
RUN(testSShrShl32(42, 8, 8));
RUN(testSShrShl32(-42, 8, 8));
RUN(testSShrShl32(4200, 8, 8));
RUN(testSShrShl32(-4200, 8, 8));
RUN(testSShrShl32(4200000, 8, 8));
RUN(testSShrShl32(-4200000, 8, 8));
RUN(testSShrShl32(420000000, 8, 8));
RUN(testSShrShl32(-420000000, 8, 8));
RUN(testSShrShl64(42, 56, 56));
RUN(testSShrShl64(-42, 56, 56));
RUN(testSShrShl64(4200, 56, 56));
RUN(testSShrShl64(-4200, 56, 56));
RUN(testSShrShl64(4200000, 56, 56));
RUN(testSShrShl64(-4200000, 56, 56));
RUN(testSShrShl64(420000000, 56, 56));
RUN(testSShrShl64(-420000000, 56, 56));
RUN(testSShrShl64(42000000000, 56, 56));
RUN(testSShrShl64(-42000000000, 56, 56));
RUN(testSShrShl64(42, 48, 48));
RUN(testSShrShl64(-42, 48, 48));
RUN(testSShrShl64(4200, 48, 48));
RUN(testSShrShl64(-4200, 48, 48));
RUN(testSShrShl64(4200000, 48, 48));
RUN(testSShrShl64(-4200000, 48, 48));
RUN(testSShrShl64(420000000, 48, 48));
RUN(testSShrShl64(-420000000, 48, 48));
RUN(testSShrShl64(42000000000, 48, 48));
RUN(testSShrShl64(-42000000000, 48, 48));
RUN(testSShrShl64(42, 32, 32));
RUN(testSShrShl64(-42, 32, 32));
RUN(testSShrShl64(4200, 32, 32));
RUN(testSShrShl64(-4200, 32, 32));
RUN(testSShrShl64(4200000, 32, 32));
RUN(testSShrShl64(-4200000, 32, 32));
RUN(testSShrShl64(420000000, 32, 32));
RUN(testSShrShl64(-420000000, 32, 32));
RUN(testSShrShl64(42000000000, 32, 32));
RUN(testSShrShl64(-42000000000, 32, 32));
RUN(testSShrShl64(42, 24, 24));
RUN(testSShrShl64(-42, 24, 24));
RUN(testSShrShl64(4200, 24, 24));
RUN(testSShrShl64(-4200, 24, 24));
RUN(testSShrShl64(4200000, 24, 24));
RUN(testSShrShl64(-4200000, 24, 24));
RUN(testSShrShl64(420000000, 24, 24));
RUN(testSShrShl64(-420000000, 24, 24));
RUN(testSShrShl64(42000000000, 24, 24));
RUN(testSShrShl64(-42000000000, 24, 24));
RUN(testSShrShl64(42, 16, 16));
RUN(testSShrShl64(-42, 16, 16));
RUN(testSShrShl64(4200, 16, 16));
RUN(testSShrShl64(-4200, 16, 16));
RUN(testSShrShl64(4200000, 16, 16));
RUN(testSShrShl64(-4200000, 16, 16));
RUN(testSShrShl64(420000000, 16, 16));
RUN(testSShrShl64(-420000000, 16, 16));
RUN(testSShrShl64(42000000000, 16, 16));
RUN(testSShrShl64(-42000000000, 16, 16));
RUN(testSShrShl64(42, 8, 8));
RUN(testSShrShl64(-42, 8, 8));
RUN(testSShrShl64(4200, 8, 8));
RUN(testSShrShl64(-4200, 8, 8));
RUN(testSShrShl64(4200000, 8, 8));
RUN(testSShrShl64(-4200000, 8, 8));
RUN(testSShrShl64(420000000, 8, 8));
RUN(testSShrShl64(-420000000, 8, 8));
RUN(testSShrShl64(42000000000, 8, 8));
RUN(testSShrShl64(-42000000000, 8, 8));
}
#endif // ENABLE(B3_JIT)