/* * 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( proc, BitAnd, Origin(), root->appendNew( proc, SExt32, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))), root->appendNew(proc, Origin(), mask))); CHECK(compileAndRun(proc, value) == (static_cast(value) & mask)); } void testBasicSelect() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 42)), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); auto code = compileProc(proc); CHECK(invoke(*code, 42, 1, 2) == 1); CHECK(invoke(*code, 42, 642462, 32533) == 642462); CHECK(invoke(*code, 43, 1, 2) == 2); CHECK(invoke(*code, 43, 642462, 32533) == 32533); } void testSelectTest() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); auto code = compileProc(proc); CHECK(invoke(*code, 42, 1, 2) == 1); CHECK(invoke(*code, 42, 642462, 32533) == 642462); CHECK(invoke(*code, 0, 1, 2) == 2); CHECK(invoke(*code, 0, 642462, 32533) == 32533); } void testSelectCompareDouble() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, LessThan, Origin(), root->appendNew(proc, Origin(), FPRInfo::argumentFPR0), root->appendNew(proc, Origin(), FPRInfo::argumentFPR1)), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); auto code = compileProc(proc); CHECK(invoke(*code, -1.0, 1.0, 1, 2) == 1); CHECK(invoke(*code, 42.5, 42.51, 642462, 32533) == 642462); CHECK(invoke(*code, PNaN, 0.0, 1, 2) == 2); CHECK(invoke(*code, 42.51, 42.5, 642462, 32533) == 32533); CHECK(invoke(*code, 42.52, 42.52, 524978245, 352) == 352); } template void testSelectCompareFloat(float a, float b, bool (*operation)(float, float)) { Procedure proc; BasicBlock* root = proc.addBlock(); Value* argument1int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* argument2int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1)); Value* floatValue1 = root->appendNew(proc, BitwiseCast, Origin(), argument1int32); Value* floatValue2 = root->appendNew(proc, BitwiseCast, Origin(), argument2int32); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, opcode, Origin(), floatValue1, floatValue2), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3))); CHECK(isIdentical(compileAndRun(proc, bitwise_cast(a), bitwise_cast(b), 42, -5), operation(a, b) ? 42 : -5)); } void testSelectCompareFloat(float a, float b) { testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a == b; }); testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a != b; }); testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a < b; }); testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a > b; }); testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a <= b; }); testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a >= b; }); testSelectCompareFloat(a, b, [](float a, float b) -> bool { return a != a || b != b || a == b; }); } template void testSelectCompareFloatToDouble(float a, float b, bool (*operation)(float, float)) { Procedure proc; BasicBlock* root = proc.addBlock(); Value* argument1int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* argument2int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1)); Value* floatValue1 = root->appendNew(proc, BitwiseCast, Origin(), argument1int32); Value* floatValue2 = root->appendNew(proc, BitwiseCast, Origin(), argument2int32); Value* doubleValue1 = root->appendNew(proc, FloatToDouble, Origin(), floatValue1); Value* doubleValue2 = root->appendNew(proc, FloatToDouble, Origin(), floatValue2); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, opcode, Origin(), doubleValue1, doubleValue2), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3))); CHECK(isIdentical(compileAndRun(proc, bitwise_cast(a), bitwise_cast(b), 42, -5), operation(a, b) ? 42 : -5)); } void testSelectCompareFloatToDouble(float a, float b) { testSelectCompareFloatToDouble(a, b, [](float a, float b) -> bool { return a == b; }); testSelectCompareFloatToDouble(a, b, [](float a, float b) -> bool { return a != b; }); testSelectCompareFloatToDouble(a, b, [](float a, float b) -> bool { return a < b; }); testSelectCompareFloatToDouble(a, b, [](float a, float b) -> bool { return a > b; }); testSelectCompareFloatToDouble(a, b, [](float a, float b) -> bool { return a <= b; }); testSelectCompareFloatToDouble(a, b, [](float a, float b) -> bool { return a >= b; }); testSelectCompareFloatToDouble(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( proc, Select, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 42)), root->appendNew(proc, Origin(), FPRInfo::argumentFPR0), root->appendNew(proc, Origin(), FPRInfo::argumentFPR1))); auto code = compileProc(proc); CHECK(invoke(*code, 42, 1.5, 2.6) == 1.5); CHECK(invoke(*code, 42, 642462.7, 32533.8) == 642462.7); CHECK(invoke(*code, 43, 1.9, 2.0) == 2.0); CHECK(invoke(*code, 43, 642462.1, 32533.2) == 32533.2); } void testSelectDoubleTest() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), FPRInfo::argumentFPR0), root->appendNew(proc, Origin(), FPRInfo::argumentFPR1))); auto code = compileProc(proc); CHECK(invoke(*code, 42, 1.5, 2.6) == 1.5); CHECK(invoke(*code, 42, 642462.7, 32533.8) == 642462.7); CHECK(invoke(*code, 0, 1.9, 2.0) == 2.0); CHECK(invoke(*code, 0, 642462.1, 32533.2) == 32533.2); } void testSelectDoubleCompareDouble() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, LessThan, Origin(), root->appendNew(proc, Origin(), FPRInfo::argumentFPR0), root->appendNew(proc, Origin(), FPRInfo::argumentFPR1)), root->appendNew(proc, Origin(), FPRInfo::argumentFPR2), root->appendNew(proc, Origin(), FPRInfo::argumentFPR3))); auto code = compileProc(proc); CHECK(invoke(*code, -1.0, 1.0, 1.1, 2.2) == 1.1); CHECK(invoke(*code, 42.5, 42.51, 642462.3, 32533.4) == 642462.3); CHECK(invoke(*code, PNaN, 0.0, 1.5, 2.6) == 2.6); CHECK(invoke(*code, 42.51, 42.5, 642462.7, 32533.8) == 32533.8); CHECK(invoke(*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(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* argument2int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1)); Value* floatValue1 = root->appendNew(proc, BitwiseCast, Origin(), argument1int32); Value* floatValue2 = root->appendNew(proc, BitwiseCast, Origin(), argument2int32); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, LessThan, Origin(), floatValue1, floatValue2), root->appendNew(proc, Origin(), FPRInfo::argumentFPR0), root->appendNew(proc, Origin(), FPRInfo::argumentFPR1))); CHECK(isIdentical(compileAndRun(proc, bitwise_cast(a), bitwise_cast(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(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* argument2int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1)); Value* argument3int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2)); Value* argument4int32 = root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3)); Value* floatValue1 = root->appendNew(proc, BitwiseCast, Origin(), argument1int32); Value* floatValue2 = root->appendNew(proc, BitwiseCast, Origin(), argument2int32); Value* floatValue3 = root->appendNew(proc, BitwiseCast, Origin(), argument3int32); Value* floatValue4 = root->appendNew(proc, BitwiseCast, Origin(), argument4int32); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, LessThan, Origin(), floatValue1, floatValue2), floatValue3, floatValue4)); CHECK(isIdentical(compileAndRun(proc, bitwise_cast(a), bitwise_cast(b), bitwise_cast(1.1f), bitwise_cast(-42.f)), a < b ? 1.1f : -42.f)); } template void testSelectDoubleCompareDouble(bool (*operation)(double, double)) { { // Compare arguments and selected arguments are all different. Procedure proc; BasicBlock* root = proc.addBlock(); Value* arg0 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR0); Value* arg1 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR1); Value* arg2 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR2); Value* arg3 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR3); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, opcode, Origin(), arg0, arg1), arg2, arg3)); auto code = compileProc(proc); for (auto& left : floatingPointOperands()) { for (auto& right : floatingPointOperands()) { double expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); Value* arg1 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR1); Value* arg2 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR2); Value* arg3 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR3); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg3); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { double expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); Value* arg1 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR1); Value* arg2 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR2); Value* arg3 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR3); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg3); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { double expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); Value* arg1 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR1); Value* arg2 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR2); Value* arg3 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR3); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg3); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { double expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); Value* arg1 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR1); Value* arg2 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR2); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, opcode, Origin(), arg0, arg1), arg2, arg0)); auto code = compileProc(proc); for (auto& left : floatingPointOperands()) { for (auto& right : floatingPointOperands()) { double expected = operation(left.value, right.value) ? 42.5 : left.value; CHECK(isIdentical(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); Value* arg1 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR1); Value* arg2 = root->appendNew(proc, Origin(), FPRInfo::argumentFPR2); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg0); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { double expected = operation(left.value, right.value) ? 42.5 : left.value; CHECK(isIdentical(invoke(*code, left.value, right.value, 42.5, left.value), expected)); } } } } void testSelectDoubleCompareDoubleWithAliasing() { testSelectDoubleCompareDouble([](double a, double b) -> bool { return a == b; }); testSelectDoubleCompareDouble([](double a, double b) -> bool { return a != b; }); testSelectDoubleCompareDouble([](double a, double b) -> bool { return a < b; }); testSelectDoubleCompareDouble([](double a, double b) -> bool { return a > b; }); testSelectDoubleCompareDouble([](double a, double b) -> bool { return a <= b; }); testSelectDoubleCompareDouble([](double a, double b) -> bool { return a >= b; }); testSelectDoubleCompareDouble([](double a, double b) -> bool { return a != a || b != b || a == b; }); } template void testSelectFloatCompareFloat(bool (*operation)(float, float)) { { // Compare arguments and selected arguments are all different. Procedure proc; BasicBlock* root = proc.addBlock(); Value* arg0 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); Value* arg1 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); Value* arg2 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); Value* arg3 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3))); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, opcode, Origin(), arg0, arg1), arg2, arg3)); auto code = compileProc(proc); for (auto& left : floatingPointOperands()) { for (auto& right : floatingPointOperands()) { float expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*code, bitwise_cast(left.value), bitwise_cast(right.value), bitwise_cast(42.5f), bitwise_cast(-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(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); Value* arg1 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); Value* arg2 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); Value* arg3 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3))); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg3); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { float expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*code, bitwise_cast(left.value), bitwise_cast(right.value), bitwise_cast(42.5f), bitwise_cast(-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(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); Value* arg1 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); Value* arg2 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); Value* arg3 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3))); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg3); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { float expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*code, bitwise_cast(left.value), bitwise_cast(right.value), bitwise_cast(42.5f), bitwise_cast(-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(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); Value* arg1 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); Value* arg2 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); Value* arg3 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3))); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg3); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { float expected = operation(left.value, right.value) ? 42.5 : -66.5; CHECK(isIdentical(invoke(*code, bitwise_cast(left.value), bitwise_cast(right.value), bitwise_cast(42.5f), bitwise_cast(-66.5f)), expected)); } } } { // The left argument is the same as the "elseCase" argument. Procedure proc; BasicBlock* root = proc.addBlock(); Value* arg0 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); Value* arg1 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); Value* arg2 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, opcode, Origin(), arg0, arg1), arg2, arg0)); auto code = compileProc(proc); for (auto& left : floatingPointOperands()) { for (auto& right : floatingPointOperands()) { float expected = operation(left.value, right.value) ? 42.5 : left.value; CHECK(isIdentical(invoke(*code, bitwise_cast(left.value), bitwise_cast(right.value), bitwise_cast(42.5f), bitwise_cast(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(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); Value* arg1 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); Value* arg2 = root->appendNew(proc, BitwiseCast, Origin(), root->appendNew(proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); Value* result = root->appendNew(proc, Select, Origin(), root->appendNew(proc, opcode, Origin(), arg0, arg1), arg2, arg0); PatchpointValue* keepValuesLive = root->appendNew(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()) { for (auto& right : floatingPointOperands()) { float expected = operation(left.value, right.value) ? 42.5 : left.value; CHECK(isIdentical(invoke(*code, bitwise_cast(left.value), bitwise_cast(right.value), bitwise_cast(42.5f), bitwise_cast(left.value)), expected)); } } } } void testSelectFloatCompareFloatWithAliasing() { testSelectFloatCompareFloat([](float a, float b) -> bool { return a == b; }); testSelectFloatCompareFloat([](float a, float b) -> bool { return a != b; }); testSelectFloatCompareFloat([](float a, float b) -> bool { return a < b; }); testSelectFloatCompareFloat([](float a, float b) -> bool { return a > b; }); testSelectFloatCompareFloat([](float a, float b) -> bool { return a <= b; }); testSelectFloatCompareFloat([](float a, float b) -> bool { return a >= b; }); testSelectFloatCompareFloat([](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( proc, Select, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew(proc, Origin(), value), root->appendNew(proc, Origin(), 42)), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); auto code = compileProc(proc); CHECK(invoke(*code, 1, 2) == (value == 42 ? 1 : 2)); CHECK(invoke(*code, 642462, 32533) == (value == 42 ? 642462 : 32533)); } void testSelectInvert() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, Equal, Origin(), root->appendNew( proc, NotEqual, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 42)), root->appendNew(proc, Origin(), 0)), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1), root->appendNew(proc, Origin(), GPRInfo::argumentGPR2))); auto code = compileProc(proc); CHECK(invoke(*code, 42, 1, 2) == 1); CHECK(invoke(*code, 42, 642462, 32533) == 642462); CHECK(invoke(*code, 43, 1, 2) == 2); CHECK(invoke(*code, 43, 642462, 32533) == 32533); } void testCheckSelect() { Procedure proc; if (proc.optLevel() < 1) return; BasicBlock* root = proc.addBlock(); CheckValue* check = root->appendNew( proc, Check, Origin(), root->appendNew( proc, Add, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew( proc, Origin(), GPRInfo::argumentGPR0)), root->appendNew(proc, Origin(), 0xff)), root->appendNew(proc, Origin(), -42), root->appendNew(proc, Origin(), 35)), root->appendNew(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(proc, Origin(), 0)); auto code = compileProc(proc); CHECK(generationCount == 1); CHECK(invoke(*code, true) == 0); CHECK(invoke(*code, false) == 666); } void testCheckSelectCheckSelect() { Procedure proc; if (proc.optLevel() < 1) return; BasicBlock* root = proc.addBlock(); CheckValue* check = root->appendNew( proc, Check, Origin(), root->appendNew( proc, Add, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew( proc, Origin(), GPRInfo::argumentGPR0)), root->appendNew(proc, Origin(), 0xff)), root->appendNew(proc, Origin(), -42), root->appendNew(proc, Origin(), 35)), root->appendNew(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( proc, Check, Origin(), root->appendNew( proc, Add, Origin(), root->appendNew( proc, Select, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew( proc, Origin(), GPRInfo::argumentGPR1)), root->appendNew(proc, Origin(), 0xff)), root->appendNew(proc, Origin(), -43), root->appendNew(proc, Origin(), 36)), root->appendNew(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(proc, Origin(), 0)); auto code = compileProc(proc); CHECK(generationCount == 1); CHECK(generationCount2 == 1); CHECK(invoke(*code, true, true) == 0); CHECK(invoke(*code, false, true) == 666); CHECK(invoke(*code, true, false) == 667); } void testCheckSelectAndCSE() { Procedure proc; if (proc.optLevel() < 1) return; BasicBlock* root = proc.addBlock(); auto* selectValue = root->appendNew( proc, Select, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew( proc, Origin(), GPRInfo::argumentGPR0)), root->appendNew(proc, Origin(), 0xff)), root->appendNew(proc, Origin(), -42), root->appendNew(proc, Origin(), 35)); auto* constant = root->appendNew(proc, Origin(), 42); auto* addValue = root->appendNew(proc, Add, Origin(), selectValue, constant); CheckValue* check = root->appendNew(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(proc, Add, Origin(), selectValue, constant); root->appendNewControlValue( proc, Return, Origin(), root->appendNew(proc, Add, Origin(), addValue, addValue2)); auto code = compileProc(proc); CHECK(generationCount == 1); CHECK(invoke(*code, true) == 0); CHECK(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); Value* y = root->appendNew(proc, Trunc, Origin(), root->appendNew(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(proc, xOperand, yOperand), b3Pow(xOperand, yOperand))); } void testTruncOrHigh() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew( proc, BitOr, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 0x100000000)))); int64_t value = 0x123456781234; CHECK(compileAndRun(proc, value) == 0x56781234); } void testTruncOrLow() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew( proc, BitOr, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 0x1000000)))); int64_t value = 0x123456781234; CHECK(compileAndRun(proc, value) == 0x57781234); } void testBitAndOrHigh() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew( proc, BitOr, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 0x8)), root->appendNew(proc, Origin(), 0x777777777777))); int64_t value = 0x123456781234; CHECK(compileAndRun(proc, value) == 0x123456701234ll); } void testBitAndOrLow() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew( proc, BitOr, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), 0x1)), root->appendNew(proc, Origin(), 0x777777777777))); int64_t value = 0x123456781234; CHECK(compileAndRun(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(proc, Origin(), GPRInfo::argumentGPR0); Value* arg2 = root->appendNew(proc, Origin(), GPRInfo::argumentGPR1); root->appendNewControlValue( proc, Branch, Origin(), root->appendNew(proc, Equal, Origin(), arg1, arg2), FrequentedBlock(thenCase), FrequentedBlock(elseCase)); bool trueResult = true; thenCase->appendNewControlValue( proc, Return, Origin(), thenCase->appendNew( proc, Load8Z, Origin(), thenCase->appendNew(proc, Origin(), &trueResult))); bool elseResult = false; elseCase->appendNewControlValue( proc, Return, Origin(), elseCase->appendNew( proc, Load8Z, Origin(), elseCase->appendNew(proc, Origin(), &elseResult))); CHECK(compileAndRun(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(proc, Origin(), GPRInfo::argumentGPR0); Value* arg2 = root->appendNew(proc, Origin(), right); root->appendNewControlValue( proc, Branch, Origin(), root->appendNew(proc, Equal, Origin(), arg1, arg2), FrequentedBlock(thenCase), FrequentedBlock(elseCase)); bool trueResult = true; thenCase->appendNewControlValue( proc, Return, Origin(), thenCase->appendNew( proc, Load8Z, Origin(), thenCase->appendNew(proc, Origin(), &trueResult))); bool elseResult = false; elseCase->appendNewControlValue( proc, Return, Origin(), elseCase->appendNew( proc, Load8Z, Origin(), elseCase->appendNew(proc, Origin(), &elseResult))); CHECK(compileAndRun(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( proc, Load, pointerType(), Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* arg2 = root->appendNew(proc, Origin(), GPRInfo::argumentGPR1); root->appendNewControlValue( proc, Branch, Origin(), root->appendNew(proc, Equal, Origin(), arg1, arg2), FrequentedBlock(thenCase), FrequentedBlock(elseCase)); bool trueResult = true; thenCase->appendNewControlValue( proc, Return, Origin(), thenCase->appendNew( proc, Load8Z, Origin(), thenCase->appendNew(proc, Origin(), &trueResult))); bool elseResult = false; elseCase->appendNewControlValue( proc, Return, Origin(), elseCase->appendNew( proc, Load8Z, Origin(), elseCase->appendNew(proc, Origin(), &elseResult))); CHECK(compileAndRun(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( proc, Load, pointerType(), Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* arg2 = root->appendNew(proc, Origin(), right); root->appendNewControlValue( proc, Branch, Origin(), root->appendNew(proc, Equal, Origin(), arg1, arg2), FrequentedBlock(thenCase), FrequentedBlock(elseCase)); bool trueResult = true; thenCase->appendNewControlValue( proc, Return, Origin(), thenCase->appendNew( proc, Load8Z, Origin(), thenCase->appendNew(proc, Origin(), &trueResult))); bool elseResult = false; elseCase->appendNewControlValue( proc, Return, Origin(), elseCase->appendNew( proc, Load8Z, Origin(), elseCase->appendNew(proc, Origin(), &elseResult))); CHECK(compileAndRun(proc, &left) == (left == right)); } void testStore8Load8Z(int32_t value) { Procedure proc; BasicBlock* root = proc.addBlock(); int8_t byte; Value* ptr = root->appendNew(proc, Origin(), &byte); root->appendNew( proc, Store8, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), ptr); root->appendNewControlValue( proc, Return, Origin(), root->appendNew(proc, Load8Z, Origin(), ptr)); CHECK(compileAndRun(proc, value) == static_cast(value)); } void testStore16Load16Z(int32_t value) { Procedure proc; BasicBlock* root = proc.addBlock(); int16_t byte; Value* ptr = root->appendNew(proc, Origin(), &byte); root->appendNew( proc, Store16, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), ptr); root->appendNewControlValue( proc, Return, Origin(), root->appendNew(proc, Load16Z, Origin(), ptr)); CHECK(compileAndRun(proc, value) == static_cast(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( proc, SShr, Origin(), root->appendNew( proc, Shl, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), root->appendNew(proc, Origin(), shlAmount)), root->appendNew(proc, Origin(), sshrAmount))); CHECK( compileAndRun(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( proc, SShr, Origin(), root->appendNew( proc, Shl, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), shlAmount)), root->appendNew(proc, Origin(), sshrAmount))); CHECK( compileAndRun(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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNewControlValue( proc, Branch, Origin(), arg, FrequentedBlock(thenBlock), FrequentedBlock(elseBlock)); thenBlock->appendNewControlValue( proc, Return, Origin(), thenBlock->appendNew( proc, Equal, Origin(), arg, thenBlock->appendNew(proc, Origin(), 0))); elseBlock->appendNewControlValue( proc, Return, Origin(), elseBlock->appendNew( proc, Equal, Origin(), arg, elseBlock->appendNew(proc, Origin(), 0))); auto code = compileProc(proc); CHECK(invoke(*code, 0) == 1); CHECK(invoke(*code, 1) == 0); CHECK(invoke(*code, 42) == 0); } void testLShiftSelf32() { Procedure proc; BasicBlock* root = proc.addBlock(); Value* arg = root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); root->appendNewControlValue( proc, Return, Origin(), root->appendNew(proc, Shl, Origin(), arg, arg)); auto code = compileProc(proc); auto check = [&] (int32_t value) { CHECK(invoke(*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( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); root->appendNewControlValue( proc, Return, Origin(), root->appendNew(proc, SShr, Origin(), arg, arg)); auto code = compileProc(proc); auto check = [&] (int32_t value) { CHECK(invoke(*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( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); root->appendNewControlValue( proc, Return, Origin(), root->appendNew(proc, ZShr, Origin(), arg, arg)); auto code = compileProc(proc); auto check = [&] (uint32_t value) { CHECK(invoke(*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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, Shl, Origin(), arg, root->appendNew(proc, Trunc, Origin(), arg))); auto code = compileProc(proc); auto check = [&] (int64_t value) { CHECK(invoke(*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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, SShr, Origin(), arg, root->appendNew(proc, Trunc, Origin(), arg))); auto code = compileProc(proc); auto check = [&] (int64_t value) { CHECK(invoke(*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(proc, Origin(), GPRInfo::argumentGPR0); root->appendNewControlValue( proc, Return, Origin(), root->appendNew( proc, ZShr, Origin(), arg, root->appendNew(proc, Trunc, Origin(), arg))); auto code = compileProc(proc); auto check = [&] (uint64_t value) { CHECK(invoke(*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(proc, Origin(), FPRInfo::argumentFPR0); PatchpointValue* patchpoint = root->appendNew(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(*code, 42.5) == 42.5); } void testSpillDefSmallerThanUse() { Procedure proc; BasicBlock* root = proc.addBlock(); // Move32. Value* arg32 = root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); Value* arg64 = root->appendNew(proc, ZExt32, Origin(), arg32); // Make sure arg64 is on the stack. PatchpointValue* forceSpill = root->appendNew(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(proc, Sub, Origin(), forceSpill, arg64); root->appendNewControlValue(proc, Return, Origin(), result); auto code = compileProc(proc); CHECK(invoke(*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(proc, Origin(), GPRInfo::argumentGPR0); Value* argument = root->appendNew(proc, Origin(), GPRInfo::argumentGPR1); root->appendNewControlValue( proc, Branch, Origin(), root->appendNew( proc, Trunc, Origin(), condition), FrequentedBlock(thenCase), FrequentedBlock(elseCase)); Value* truncated = thenCase->appendNew(proc, ZExt32, Origin(), thenCase->appendNew(proc, Trunc, Origin(), argument)); UpsilonValue* thenResult = thenCase->appendNew(proc, Origin(), truncated); thenCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail)); UpsilonValue* elseResult = elseCase->appendNew(proc, Origin(), argument); elseCase->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(tail)); for (unsigned i = 0; i < 100; ++i) { PatchpointValue* preventTailDuplication = tail->appendNew(proc, Void, Origin()); preventTailDuplication->clobberLate(clobberSet); preventTailDuplication->setGenerator([] (CCallHelpers&, const StackmapGenerationParams&) { }); } PatchpointValue* forceSpill = tail->appendNew(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(proc, Phi, Int64, Origin()); thenResult->setPhi(phi); elseResult->setPhi(phi); tail->appendNewControlValue(proc, Return, Origin(), phi); auto code = compileProc(proc); CHECK(invoke(*code, 1, 0xffffffff00000000) == 0); CHECK(invoke(*code, 0, 0xffffffff00000000) == 0xffffffff00000000); // A second time since the previous run is still on the stack. CHECK(invoke(*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 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(proc, Origin(), 1); lateUseArgs.append(value); } Value* regularUse = root->appendNew(proc, Origin(), 1); PatchpointValue* firstPatchpoint = root->appendNew(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(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(*code) == result); } JSC_DECLARE_JIT_OPERATION(interpreterPrint, void, (Vector* stream, intptr_t value)); JSC_DEFINE_JIT_OPERATION(interpreterPrint, void, (Vector* 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( proc, Set, Origin(), dataPointer, root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); root->appendNew( proc, Set, Origin(), codePointer, root->appendNew(proc, Origin(), GPRInfo::argumentGPR1)); Value* context = root->appendNew(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(proc, B3::Get, Origin(), codePointer); Value* opcode = dispatch->appendNew( proc, Load, pointerType(), Origin(), codePointerValue); PatchpointValue* polyJump = dispatch->appendNew(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> labels = params.successorLabels(); MacroAssemblerCodePtr* jumpTable = bitwise_cast*>( params.proc().addDataSection(sizeof(MacroAssemblerCodePtr) * 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(*labels[i]); }); }); // AddDP : adds to DP. codePointerValue = addToDataPointer->appendNew(proc, B3::Get, Origin(), codePointer); addToDataPointer->appendNew( proc, Set, Origin(), dataPointer, addToDataPointer->appendNew( proc, B3::Add, Origin(), addToDataPointer->appendNew(proc, B3::Get, Origin(), dataPointer), addToDataPointer->appendNew( proc, Mul, Origin(), addToDataPointer->appendNew( proc, Load, pointerType(), Origin(), codePointerValue, static_cast(sizeof(intptr_t))), addToDataPointer->appendIntConstant( proc, Origin(), pointerType(), sizeof(intptr_t))))); addToDataPointer->appendNew( proc, Set, Origin(), codePointer, addToDataPointer->appendNew( proc, B3::Add, Origin(), codePointerValue, addToDataPointer->appendIntConstant( proc, Origin(), pointerType(), sizeof(intptr_t) * 2))); addToDataPointer->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch)); // AddCP : adds to CP if the current value at DP is non-zero, otherwise // falls through normally. codePointerValue = addToCodePointer->appendNew(proc, B3::Get, Origin(), codePointer); Value* dataPointerValue = addToCodePointer->appendNew(proc, B3::Get, Origin(), dataPointer); addToCodePointer->appendNewControlValue( proc, Branch, Origin(), addToCodePointer->appendNew( proc, Load, pointerType(), Origin(), dataPointerValue), FrequentedBlock(addToCodePointerTaken), FrequentedBlock(addToCodePointerNotTaken)); addToCodePointerTaken->appendNew( proc, Set, Origin(), codePointer, addToCodePointerTaken->appendNew( proc, B3::Add, Origin(), codePointerValue, addToCodePointerTaken->appendNew( proc, Mul, Origin(), addToCodePointerTaken->appendNew( proc, Load, pointerType(), Origin(), codePointerValue, static_cast(sizeof(intptr_t))), addToCodePointerTaken->appendIntConstant( proc, Origin(), pointerType(), sizeof(intptr_t))))); addToCodePointerTaken->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(dispatch)); addToCodePointerNotTaken->appendNew( proc, Set, Origin(), codePointer, addToCodePointerNotTaken->appendNew( proc, B3::Add, Origin(), codePointerValue, addToCodePointerNotTaken->appendIntConstant( proc, Origin(), pointerType(), sizeof(intptr_t) * 2))); addToCodePointerNotTaken->appendNewControlValue( proc, Jump, Origin(), FrequentedBlock(dispatch)); // Add : adds to the slot pointed to by DP. codePointerValue = addToData->appendNew(proc, B3::Get, Origin(), codePointer); dataPointerValue = addToData->appendNew(proc, B3::Get, Origin(), dataPointer); addToData->appendNew( proc, Store, Origin(), addToData->appendNew( proc, B3::Add, Origin(), addToData->appendNew( proc, Load, pointerType(), Origin(), dataPointerValue), addToData->appendNew( proc, Load, pointerType(), Origin(), codePointerValue, static_cast(sizeof(intptr_t)))), dataPointerValue); addToData->appendNew( proc, Set, Origin(), codePointer, addToData->appendNew( 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(proc, B3::Get, Origin(), codePointer); dataPointerValue = print->appendNew(proc, B3::Get, Origin(), dataPointer); print->appendNew( proc, Void, Origin(), print->appendNew( proc, Origin(), tagCFunction(interpreterPrint)), context, print->appendNew(proc, Load, pointerType(), Origin(), dataPointerValue)); print->appendNew( proc, Set, Origin(), codePointer, print->appendNew( 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 data; Vector code; Vector 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(*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( proc, Check, Origin(), one->appendNew(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(proc, Origin(), GPRInfo::argumentGPR0); one->appendNewControlValue(proc, Jump, Origin(), FrequentedBlock(two)); check = two->appendNew( proc, CheckAdd, Origin(), arg, two->appendNew(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( proc, Origin(), one->appendNew(proc, Origin(), 42)); one->appendNewControlValue(proc, Oops, Origin()); Value* phi = two->appendNew(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(proc, EntrySwitch, Origin()); root->appendSuccessor(FrequentedBlock(one)); root->appendSuccessor(FrequentedBlock(two)); root->appendSuccessor(FrequentedBlock(three)); one->appendNew( proc, Return, Origin(), one->appendNew( proc, Add, Origin(), one->appendNew(proc, Origin(), GPRInfo::argumentGPR0), one->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); two->appendNew( proc, Return, Origin(), two->appendNew( proc, Sub, Origin(), two->appendNew(proc, Origin(), GPRInfo::argumentGPR0), two->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); three->appendNew( proc, Return, Origin(), three->appendNew( proc, Mul, Origin(), three->appendNew(proc, Origin(), GPRInfo::argumentGPR0), three->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); prepareForGeneration(proc); CCallHelpers jit; generate(proc, jit); LinkBuffer linkBuffer(jit, nullptr); CodeLocationLabel labelOne = linkBuffer.locationOf(proc.code().entrypointLabel(0)); CodeLocationLabel labelTwo = linkBuffer.locationOf(proc.code().entrypointLabel(1)); CodeLocationLabel labelThree = linkBuffer.locationOf(proc.code().entrypointLabel(2)); MacroAssemblerCodeRef codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation"); CHECK(invoke(labelOne, 1, 2) == 3); CHECK(invoke(labelTwo, 1, 2) == -1); CHECK(invoke(labelThree, 1, 2) == 2); CHECK(invoke(labelOne, -1, 2) == 1); CHECK(invoke(labelTwo, -1, 2) == -3); CHECK(invoke(labelThree, -1, 2) == -2); } void testEntrySwitchNoEntrySwitch() { Procedure proc; proc.setNumEntrypoints(3); BasicBlock* root = proc.addBlock(); root->appendNew( proc, Return, Origin(), root->appendNew( proc, Add, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0), root->appendNew(proc, Origin(), GPRInfo::argumentGPR1))); prepareForGeneration(proc); CCallHelpers jit; generate(proc, jit); LinkBuffer linkBuffer(jit, nullptr); CodeLocationLabel labelOne = linkBuffer.locationOf(proc.code().entrypointLabel(0)); CodeLocationLabel labelTwo = linkBuffer.locationOf(proc.code().entrypointLabel(1)); CodeLocationLabel labelThree = linkBuffer.locationOf(proc.code().entrypointLabel(2)); MacroAssemblerCodeRef codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation"); CHECK_EQ(invoke(labelOne, 1, 2), 3); CHECK_EQ(invoke(labelTwo, 1, 2), 3); CHECK_EQ(invoke(labelThree, 1, 2), 3); CHECK_EQ(invoke(labelOne, -1, 2), 1); CHECK_EQ(invoke(labelTwo, -1, 2), 1); CHECK_EQ(invoke(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(proc, EntrySwitch, Origin()); root->appendSuccessor(FrequentedBlock(one)); root->appendSuccessor(FrequentedBlock(two)); root->appendSuccessor(FrequentedBlock(three)); UpsilonValue* upsilonOne = one->appendNew( proc, Origin(), one->appendNew( proc, Add, Origin(), one->appendNew( proc, Trunc, Origin(), one->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), one->appendNew( proc, Trunc, Origin(), one->appendNew(proc, Origin(), GPRInfo::argumentGPR1)))); one->appendNew(proc, Jump, Origin()); one->setSuccessors(FrequentedBlock(end)); UpsilonValue* upsilonTwo = two->appendNew( proc, Origin(), two->appendNew( proc, Sub, Origin(), two->appendNew( proc, Trunc, Origin(), two->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), two->appendNew( proc, Trunc, Origin(), two->appendNew(proc, Origin(), GPRInfo::argumentGPR1)))); two->appendNew(proc, Jump, Origin()); two->setSuccessors(FrequentedBlock(end)); UpsilonValue* upsilonThree = three->appendNew( proc, Origin(), three->appendNew( proc, Mul, Origin(), three->appendNew( proc, Trunc, Origin(), three->appendNew(proc, Origin(), GPRInfo::argumentGPR0)), three->appendNew( proc, Trunc, Origin(), three->appendNew(proc, Origin(), GPRInfo::argumentGPR1)))); three->appendNew(proc, Jump, Origin()); three->setSuccessors(FrequentedBlock(end)); Value* phi = end->appendNew(proc, Phi, Int32, Origin()); upsilonOne->setPhi(phi); upsilonTwo->setPhi(phi); upsilonThree->setPhi(phi); end->appendNew( proc, Return, Origin(), end->appendNew( proc, chill(Mod), Origin(), phi, end->appendNew( proc, Trunc, Origin(), end->appendNew(proc, Origin(), GPRInfo::argumentGPR2)))); prepareForGeneration(proc); CCallHelpers jit; generate(proc, jit); LinkBuffer linkBuffer(jit, nullptr); CodeLocationLabel labelOne = linkBuffer.locationOf(proc.code().entrypointLabel(0)); CodeLocationLabel labelTwo = linkBuffer.locationOf(proc.code().entrypointLabel(1)); CodeLocationLabel labelThree = linkBuffer.locationOf(proc.code().entrypointLabel(2)); MacroAssemblerCodeRef codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation"); CHECK_EQ(invoke(labelOne, 1, 2, 10), 3); CHECK_EQ(invoke(labelTwo, 1, 2, 10), -1); CHECK_EQ(invoke(labelThree, 1, 2, 10), 2); CHECK_EQ(invoke(labelOne, -1, 2, 10), 1); CHECK_EQ(invoke(labelTwo, -1, 2, 10), -3); CHECK_EQ(invoke(labelThree, -1, 2, 10), -2); CHECK_EQ(invoke(labelOne, 1, 2, 2), 1); CHECK_EQ(invoke(labelTwo, 1, 2, 2), -1); CHECK_EQ(invoke(labelThree, 1, 2, 2), 0); CHECK_EQ(invoke(labelOne, -1, 2, 2), 1); CHECK_EQ(invoke(labelTwo, -1, 2, 2), -1); CHECK_EQ(invoke(labelThree, -1, 2, 2), 0); CHECK_EQ(invoke(labelOne, 1, 2, 0), 0); CHECK_EQ(invoke(labelTwo, 1, 2, 0), 0); CHECK_EQ(invoke(labelThree, 1, 2, 0), 0); CHECK_EQ(invoke(labelOne, -1, 2, 0), 0); CHECK_EQ(invoke(labelTwo, -1, 2, 0), 0); CHECK_EQ(invoke(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( proc, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); root->appendNew( proc, Branch, Origin(), root->appendNew( proc, BitAnd, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR3), root->appendNew(proc, Origin(), 0xff))); root->setSuccessors(FrequentedBlock(negate), FrequentedBlock(dispatch)); UpsilonValue* upsilonNegate = negate->appendNew( proc, Origin(), negate->appendNew( proc, Neg, Origin(), negate->appendNew( proc, Trunc, Origin(), negate->appendNew(proc, Origin(), GPRInfo::argumentGPR0)))); negate->appendNew(proc, Jump, Origin()); negate->setSuccessors(FrequentedBlock(dispatch)); Value* arg0 = dispatch->appendNew(proc, Phi, Int32, Origin()); upsilonBase->setPhi(arg0); upsilonNegate->setPhi(arg0); dispatch->appendNew(proc, EntrySwitch, Origin()); dispatch->appendSuccessor(FrequentedBlock(one)); dispatch->appendSuccessor(FrequentedBlock(two)); dispatch->appendSuccessor(FrequentedBlock(three)); UpsilonValue* upsilonOne = one->appendNew( proc, Origin(), one->appendNew( proc, Add, Origin(), arg0, one->appendNew( proc, Trunc, Origin(), one->appendNew(proc, Origin(), GPRInfo::argumentGPR1)))); one->appendNew(proc, Jump, Origin()); one->setSuccessors(FrequentedBlock(end)); UpsilonValue* upsilonTwo = two->appendNew( proc, Origin(), two->appendNew( proc, Sub, Origin(), arg0, two->appendNew( proc, Trunc, Origin(), two->appendNew(proc, Origin(), GPRInfo::argumentGPR1)))); two->appendNew(proc, Jump, Origin()); two->setSuccessors(FrequentedBlock(end)); UpsilonValue* upsilonThree = three->appendNew( proc, Origin(), three->appendNew( proc, Mul, Origin(), arg0, three->appendNew( proc, Trunc, Origin(), three->appendNew(proc, Origin(), GPRInfo::argumentGPR1)))); three->appendNew(proc, Jump, Origin()); three->setSuccessors(FrequentedBlock(end)); Value* phi = end->appendNew(proc, Phi, Int32, Origin()); upsilonOne->setPhi(phi); upsilonTwo->setPhi(phi); upsilonThree->setPhi(phi); end->appendNew( proc, Return, Origin(), end->appendNew( proc, chill(Mod), Origin(), phi, end->appendNew( proc, Trunc, Origin(), end->appendNew(proc, Origin(), GPRInfo::argumentGPR2)))); prepareForGeneration(proc); CCallHelpers jit; generate(proc, jit); LinkBuffer linkBuffer(jit, nullptr); CodeLocationLabel labelOne = linkBuffer.locationOf(proc.code().entrypointLabel(0)); CodeLocationLabel labelTwo = linkBuffer.locationOf(proc.code().entrypointLabel(1)); CodeLocationLabel labelThree = linkBuffer.locationOf(proc.code().entrypointLabel(2)); MacroAssemblerCodeRef codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation"); CHECK_EQ(invoke(labelOne, 1, 2, 10, false), 3); CHECK_EQ(invoke(labelTwo, 1, 2, 10, false), -1); CHECK_EQ(invoke(labelThree, 1, 2, 10, false), 2); CHECK_EQ(invoke(labelOne, -1, 2, 10, false), 1); CHECK_EQ(invoke(labelTwo, -1, 2, 10, false), -3); CHECK_EQ(invoke(labelThree, -1, 2, 10, false), -2); CHECK_EQ(invoke(labelOne, 1, 2, 10, true), 1); CHECK_EQ(invoke(labelTwo, 1, 2, 10, true), -3); CHECK_EQ(invoke(labelThree, 1, 2, 10, true), -2); CHECK_EQ(invoke(labelOne, -1, 2, 10, true), 3); CHECK_EQ(invoke(labelTwo, -1, 2, 10, true), -1); CHECK_EQ(invoke(labelThree, -1, 2, 10, true), 2); CHECK_EQ(invoke(labelOne, 1, 2, 2, false), 1); CHECK_EQ(invoke(labelTwo, 1, 2, 2, false), -1); CHECK_EQ(invoke(labelThree, 1, 2, 2, false), 0); CHECK_EQ(invoke(labelOne, -1, 2, 2, false), 1); CHECK_EQ(invoke(labelTwo, -1, 2, 2, false), -1); CHECK_EQ(invoke(labelThree, -1, 2, 2, false), 0); CHECK_EQ(invoke(labelOne, 1, 2, 0, false), 0); CHECK_EQ(invoke(labelTwo, 1, 2, 0, false), 0); CHECK_EQ(invoke(labelThree, 1, 2, 0, false), 0); CHECK_EQ(invoke(labelOne, -1, 2, 0, false), 0); CHECK_EQ(invoke(labelTwo, -1, 2, 0, false), 0); CHECK_EQ(invoke(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( proc, Origin(), root->appendNew( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0))); root->appendNew(proc, Jump, Origin()); root->setSuccessors(loopHeader); Value* valueInLoop = loopHeader->appendNew(proc, Phi, Int32, Origin()); initialValue->setPhi(valueInLoop); Value* newValue = loopHeader->appendNew( proc, Add, Origin(), valueInLoop, loopHeader->appendNew(proc, Origin(), 1)); loopHeader->appendNew(proc, EntrySwitch, Origin()); loopHeader->appendSuccessor(end); loopHeader->appendSuccessor(loopFooter); loopFooter->appendNew(proc, Origin(), newValue, valueInLoop); loopFooter->appendNew( proc, Branch, Origin(), loopFooter->appendNew( proc, LessThan, Origin(), newValue, loopFooter->appendNew(proc, Origin(), 100))); loopFooter->setSuccessors(loopHeader, end); end->appendNew(proc, Return, Origin(), newValue); prepareForGeneration(proc); CCallHelpers jit; generate(proc, jit); LinkBuffer linkBuffer(jit, nullptr); CodeLocationLabel labelOne = linkBuffer.locationOf(proc.code().entrypointLabel(0)); CodeLocationLabel labelTwo = linkBuffer.locationOf(proc.code().entrypointLabel(1)); MacroAssemblerCodeRef codeRef = FINALIZE_CODE(linkBuffer, B3CompilationPtrTag, "testb3 compilation"); CHECK(invoke(labelOne, 0) == 1); CHECK(invoke(labelOne, 42) == 43); CHECK(invoke(labelOne, 1000) == 1001); CHECK(invoke(labelTwo, 0) == 100); CHECK(invoke(labelTwo, 42) == 100); CHECK(invoke(labelTwo, 1000) == 1001); } void testSomeEarlyRegister() { auto run = [&] (bool succeed) { Procedure proc; BasicBlock* root = proc.addBlock(); PatchpointValue* patchpoint = root->appendNew(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(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(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(proc, Origin(), GPRInfo::argumentGPR0); if (valueModifier != Identity) { if (MemoryValue::accepts(valueModifier)) left = root->appendNew(proc, valueModifier, valueType, Origin(), left); else left = root->appendNew(proc, valueModifier, valueType, Origin(), left); } root->appendNew( proc, Branch, Origin(), root->appendNew( proc, BitAnd, Origin(), left, root->appendIntConstant(proc, Origin(), valueType, constant))); root->setSuccessors(FrequentedBlock(one), FrequentedBlock(two)); one->appendNew(proc, Oops, Origin()); two->appendNew(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(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> labels = params.successorLabels(); params.addLatePath( [=] (CCallHelpers& jit) { if (jumpToSuccess.isSet()) jumpToSuccess.linkTo(*labels[0], &jit); }); }); Vector args; { RegisterSet fillAllGPRsSet = proc.mutableGPRs(); for (unsigned i = 0; i < fillAllGPRsSet.numberOfSetRegisters(); i++) args.append(success->appendNew(proc, Origin(), i)); } { // Now force all values into every available register. PatchpointValue* p = success->appendNew(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(proc, Void, Origin()); p->append(patchpoint, ValueRep::SomeRegister); p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { }); } success->appendNew(proc, Return, Origin(), success->appendNew(proc, Origin(), 10)); slowPath->appendNew(proc, Return, Origin(), slowPath->appendNew(proc, Origin(), 20)); auto code = compileProc(proc); CHECK_EQ(invoke(*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( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); root->appendNew( proc, Branch, Origin(), arg); root->appendSuccessor(one); root->appendSuccessor(FrequentedBlock(slowPath, FrequencyClass::Rare)); PatchpointValue* patchpoint = one->appendNew(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> labels = params.successorLabels(); params.addLatePath( [=] (CCallHelpers& jit) { goToFastPath.linkTo(*labels[0], &jit); jumpToSlow.linkTo(*labels[1], &jit); }); }); Vector args; { RegisterSet fillAllGPRsSet = proc.mutableGPRs(); for (unsigned i = 0; i < fillAllGPRsSet.numberOfSetRegisters(); i++) args.append(success->appendNew(proc, Origin(), i)); } { // Now force all values into every available register. PatchpointValue* p = success->appendNew(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(proc, Void, Origin()); p->append(patchpoint, ValueRep::SomeRegister); p->setGenerator([&] (CCallHelpers&, const StackmapGenerationParams&) { }); } success->appendNew(proc, Return, Origin(), patchpoint); slowPath->appendNew(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(*code, 1), 1); CHECK_EQ(invoke(*code, 0), 0); CHECK_EQ(invoke(*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( proc, Trunc, Origin(), root->appendNew(proc, Origin(), GPRInfo::argumentGPR0)); PatchpointValue* patchpoint = root->appendNew(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> labels = params.successorLabels(); params.addLatePath( [=] (CCallHelpers& jit) { jumpToSlow.linkTo(*labels[1], &jit); if (jumpToSuccess.isSet()) jumpToSuccess.linkTo(*labels[0], &jit); }); }); UpsilonValue* successUpsilon = success->appendNew(proc, Origin(), patchpoint); success->appendNew(proc, Jump, Origin()); success->setSuccessors(continuation); UpsilonValue* slowPathUpsilon = slowPath->appendNew( proc, Origin(), slowPath->appendNew(proc, Origin(), 666)); slowPath->appendNew(proc, Jump, Origin()); slowPath->setSuccessors(continuation); Value* phi = continuation->appendNew(proc, Phi, Int32, Origin()); successUpsilon->setPhi(phi); slowPathUpsilon->setPhi(phi); continuation->appendNew(proc, Return, Origin(), phi); auto code = compileProc(proc); CHECK_EQ(invoke(*code, 0), 31); CHECK_EQ(invoke(*code, 1), 32); CHECK_EQ(invoke(*code, 41), 72); CHECK_EQ(invoke(*code, 42), 73); CHECK_EQ(invoke(*code, 43), 666); CHECK_EQ(invoke(*code, -1), 666); } void testMemoryFence() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew(proc, Origin()); root->appendNew(proc, Return, Origin(), root->appendIntConstant(proc, Origin(), Int32, 42)); auto code = compileProc(proc); CHECK_EQ(invoke(*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(proc, Origin(), HeapRange::top(), HeapRange()); root->appendNew(proc, Return, Origin(), root->appendIntConstant(proc, Origin(), Int32, 42)); auto code = compileProc(proc); CHECK_EQ(invoke(*code), 42); checkDoesNotUseInstruction(*code, "lock"); checkDoesNotUseInstruction(*code, "mfence"); if (isARM64()) checkUsesInstruction(*code, "dmb ishst"); } void testLoadFence() { Procedure proc; BasicBlock* root = proc.addBlock(); root->appendNew(proc, Origin(), HeapRange(), HeapRange::top()); root->appendNew(proc, Return, Origin(), root->appendIntConstant(proc, Origin(), Int32, 42)); auto code = compileProc(proc); CHECK_EQ(invoke(*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( proc, trapping(Load), Int32, Origin(), root->appendNew(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(proc, Return, Origin(), value); CHECK_EQ(compileAndRun(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( proc, trapping(Store), Origin(), root->appendNew(proc, Origin(), 111), root->appendNew(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(proc, Return, Origin()); compileAndRun(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(proc, Origin(), &x); root->appendNew( proc, trapping(Store), Origin(), root->appendNew( proc, Add, Origin(), root->appendNew(proc, trapping(Load), Int32, Origin(), ptr), root->appendNew(proc, Origin(), 3)), ptr, 0); root->appendNew(proc, Return, Origin()); compileAndRun(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( proc, trapping(Load), Int32, Origin(), root->appendNew(proc, Origin(), &x)); root->appendNew(proc, Return, Origin()); compileAndRun(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(proc, Origin(), &x); root->appendNew( proc, trapping(Store), Origin(), root->appendNew(proc, Origin(), 43), ptr); root->appendNew( proc, trapping(Store), Origin(), root->appendNew(proc, Origin(), 44), ptr); root->appendNew(proc, Return, Origin()); compileAndRun(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( proc, Load, pointerType(), Origin(), root->appendNew(proc, Origin(), 0x123412341234)); Value* b = root->appendNew( proc, Load, pointerType(), Origin(), root->appendNew(proc, Origin(), 0x123412341334)); root->appendNew(proc, Void, Origin(), a, b); root->appendNew(proc, Return, Origin()); check(proc); } { Procedure proc; BasicBlock* root = proc.addBlock(); Value* x = root->appendNew(proc, Origin(), GPRInfo::argumentGPR0); Value* a = root->appendNew( proc, Add, Origin(), x, root->appendNew(proc, Origin(), 0x123412341234)); Value* b = root->appendNew( proc, Add, Origin(), x, root->appendNew(proc, Origin(), -0x123412341234)); root->appendNew(proc, Void, Origin(), a, b); root->appendNew(proc, Return, Origin()); check(proc); } } void testPCOriginMapDoesntInsertNops() { Procedure proc; BasicBlock* root = proc.addBlock(); CCallHelpers::Label watchpointLabel; PatchpointValue* patchpoint = root->appendNew(proc, Void, Origin()); patchpoint->setGenerator( [&] (CCallHelpers& jit, const StackmapGenerationParams&) { watchpointLabel = jit.watchpointLabel(); }); patchpoint = root->appendNew(proc, Void, Origin()); patchpoint->setGenerator( [&] (CCallHelpers& jit, const StackmapGenerationParams&) { CCallHelpers::Label labelIgnoringWatchpoints = jit.labelIgnoringWatchpoints(); CHECK(watchpointLabel == labelIgnoringWatchpoints); }); root->appendNew(proc, Return, Origin()); compileProc(proc); } void addSShrShTests(const char* filter, Deque>>& 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)