diff --git a/ecmascript/compiler/fast_stub.cpp b/ecmascript/compiler/fast_stub.cpp index 02c44a5804477149d91bf4c996dff5b467d29e73..ac005e5b53c0a9d3b7c40941583eae2ff1687815 100644 --- a/ecmascript/compiler/fast_stub.cpp +++ b/ecmascript/compiler/fast_stub.cpp @@ -1007,4 +1007,155 @@ void FastGetPropertyByNameStub::GenerateCircuit() } } } + +void FastModStub::GenerateCircuit() +{ + auto env = GetEnvironment(); + AddrShift x = Int64Argument(0); + AddrShift y = Int64Argument(1); + DEFVARIABLE(intX, MachineType::INT32_TYPE, 0); + DEFVARIABLE(intY, MachineType::INT32_TYPE, 0); + DEFVARIABLE(doubleX, MachineType::FLOAT64_TYPE, 0); + DEFVARIABLE(doubleY, MachineType::FLOAT64_TYPE, 0); + Label xIsInt(env); + Label xNotIntOryNotInt(env); + Branch(TaggedIsInt(x), &xIsInt, &xNotIntOryNotInt); + Bind(&xIsInt); + { + Label yIsInt(env); + Label xIsIntAndyIsInt(env); + // if right.IsInt() + Branch(TaggedIsInt(y), &yIsInt, &xNotIntOryNotInt); + Bind(&yIsInt); + { + intX = TaggedCastToInt32(x); + intY = TaggedCastToInt32(y); + Jump(&xIsIntAndyIsInt); + } + Bind(&xIsIntAndyIsInt); + { + Label xGtZero(env); + Label xGtZeroAndyGtZero(env); + Branch(Int32GreaterThan(*intX, GetInteger32Constant(0)), &xGtZero, &xNotIntOryNotInt); + Bind(&xGtZero); + { + Branch(Int32GreaterThan(*intY, GetInteger32Constant(0)), &xGtZeroAndyGtZero, &xNotIntOryNotInt); + Bind(&xGtZeroAndyGtZero); + { + intX = Int32Mod(*intX, *intY); + Return(IntBuildTagged(*intX)); + } + } + } + } + Bind(&xNotIntOryNotInt); + { + Label xIsNumber(env); + Label xNotNumberOryNotNumber(env); + Label xIsNumberAndyIsNumber(env); + Label xIsDoubleAndyIsDouble(env); + Branch(TaggedIsNumber(x), &xIsNumber, &xNotNumberOryNotNumber); + Bind(&xIsNumber); + { + Label yIsNumber(env); + // if right.IsNumber() + Branch(TaggedIsNumber(y), &yIsNumber, &xNotNumberOryNotNumber); + Bind(&yIsNumber); + { + Label xIfInt(env); + Label xIfNotInt(env); + Branch(TaggedIsInt(x), &xIfInt, &xIfNotInt); + Bind(&xIfInt); + { + intX = TaggedCastToInt32(x); + doubleX = CastInt32ToFloat64(*intX); + Jump(&xIsNumberAndyIsNumber); + } + Bind(&xIfNotInt); + { + doubleX = TaggedCastToDouble(x); + Jump(&xIsNumberAndyIsNumber); + } + } + } + Bind(&xNotNumberOryNotNumber); + Return(GetHoleConstant()); + Label yIfInt(env); + Label yIfNotInt(env); + Bind(&xIsNumberAndyIsNumber); + Branch(TaggedIsInt(y), &yIfInt, &yIfNotInt); + Bind(&yIfInt); + { + intY = TaggedCastToInt32(y); + doubleY = CastInt32ToFloat64(*intY); + Jump(&xIsDoubleAndyIsDouble); + } + Bind(&yIfNotInt); + { + doubleY = TaggedCastToDouble(y); + Jump(&xIsDoubleAndyIsDouble); + } + Bind(&xIsDoubleAndyIsDouble); + { + Label yIsZero(env); + Label yNotZero(env); + Label yIsZeroOrNanOrxIsNanOrInf(env); + Label yNeiZeroOrNanAndxNeiNanOrInf(env); + // dRight == 0.0 or std::isnan(dRight) or std::isnan(dLeft) or std::isinf(dLeft) + Branch(DoubleEqual(*doubleY, GetDoubleConstant(0.0)), &yIsZero, &yNotZero); + Bind(&yIsZero); + Jump(&yIsZeroOrNanOrxIsNanOrInf); + Bind(&yNotZero); + { + Label yIsNan(env); + Label yNotNan(env); + Branch(DoubleIsNAN(*doubleY), &yIsNan, &yNotNan); + Bind(&yIsNan); + Jump(&yIsZeroOrNanOrxIsNanOrInf); + Bind(&yNotNan); + { + Label xIsNan(env); + Label xNotNan(env); + Branch(DoubleIsNAN(*doubleX), &xIsNan, &xNotNan); + Bind(&xIsNan); + Jump(&yIsZeroOrNanOrxIsNanOrInf); + Bind(&xNotNan); + { + Label xIsInf(env); + Label xNotInf(env); + Branch(DoubleIsINF(*doubleX), &xIsInf, &xNotInf); + Bind(&xIsInf); + Jump(&yIsZeroOrNanOrxIsNanOrInf); + Bind(&xNotInf); + Jump(&yNeiZeroOrNanAndxNeiNanOrInf); + } + } + } + Bind(&yIsZeroOrNanOrxIsNanOrInf); + Return(DoubleBuildTagged(GetDoubleConstant(base::NAN_VALUE))); + Bind(&yNeiZeroOrNanAndxNeiNanOrInf); + { + Label xIsFloatZero(env); + Label xIsZeroOryIsInf(env); + Label xNotZeroAndyNotInf(env); + Branch(DoubleEqual(*doubleX, GetDoubleConstant(0.0)), &xIsFloatZero, &xNotZeroAndyNotInf); + Bind(&xIsFloatZero); + Jump(&xIsZeroOryIsInf); + Label yIsInf(env); + Label yNotInf(env); + Bind(&xNotZeroAndyNotInf); + Branch(DoubleIsINF(*doubleY), &yIsInf, &yNotInf); + Bind(&yIsInf); + Jump(&xIsZeroOryIsInf); + Bind(&yNotInf); + { + doubleX = DoubleMod(*doubleX, *doubleY); + Return(DoubleBuildTagged(*doubleX)); + } + Bind(&xIsZeroOryIsInf); + Return(DoubleBuildTagged(*doubleX)); + } + } + } +} } // namespace kungfu \ No newline at end of file diff --git a/ecmascript/compiler/fast_stub.h b/ecmascript/compiler/fast_stub.h index 940c4dc1b0a5e1bd655db53d43db4fac459c3a8a..6814fa37c7eb71f9d6dd577b292a622817038769 100644 --- a/ecmascript/compiler/fast_stub.h +++ b/ecmascript/compiler/fast_stub.h @@ -139,5 +139,15 @@ public: NO_COPY_SEMANTIC(FastGetPropertyByNameStub); void GenerateCircuit() override; }; + +class FastModStub : public Stub { +public: + // 2 means argument counts + explicit FastModStub(Circuit *circuit) : Stub("FastMod", 2, circuit) {} + ~FastModStub() = default; + NO_MOVE_SEMANTIC(FastModStub); + NO_COPY_SEMANTIC(FastModStub); + void GenerateCircuit() override; +}; } // namespace kungfu #endif // ECMASCRIPT_COMPILER_FASTPATH_STUB_H \ No newline at end of file diff --git a/ecmascript/compiler/gate.cpp b/ecmascript/compiler/gate.cpp index 747eb83c68b694562bc2e1eb34f735c056c3321e..f2e09834fd7cd10388850bd7a88853a39aa1ec4e 100644 --- a/ecmascript/compiler/gate.cpp +++ b/ecmascript/compiler/gate.cpp @@ -260,6 +260,7 @@ Properties OpCode::GetProperties() const case FLOAT64_MUL: case FLOAT64_DIV: case FLOAT64_EXP: + case FLOAT64_SMOD: return {FLOAT64, NO_STATE, NO_DEPEND, VALUE(FLOAT64, FLOAT64), NO_ROOT}; case FLOAT64_EQ: return {INT1, NO_STATE, NO_DEPEND, VALUE(FLOAT64, FLOAT64), NO_ROOT}; diff --git a/ecmascript/compiler/gate.h b/ecmascript/compiler/gate.h index 61bea73ea49008a18fb7e0a46f3fdb480bf324a2..a112479593b8bf378cf7639eb60a96ab746a5cd9 100644 --- a/ecmascript/compiler/gate.h +++ b/ecmascript/compiler/gate.h @@ -210,6 +210,7 @@ public: FLOAT64_DIV, FLOAT64_EXP, FLOAT64_EQ, + FLOAT64_SMOD, INT8_LOAD, INT16_LOAD, INT32_LOAD, diff --git a/ecmascript/compiler/llvm_ir_builder.cpp b/ecmascript/compiler/llvm_ir_builder.cpp index 2592e610c30a66dd0c9569abace37fcc51800bc4..388aed1e90bc7dc1f42f16bcf19e5c5b46ed1fb0 100644 --- a/ecmascript/compiler/llvm_ir_builder.cpp +++ b/ecmascript/compiler/llvm_ir_builder.cpp @@ -231,7 +231,8 @@ void LLVMIRBuilder::Build() } case OpCode::FLOAT64_CONSTANT: { int64_t value = circuit_->GetBitField(gate); - VisitFloat64Constant(gate, value); + double doubleValue = bit_cast(value); // actual double value + VisitFloat64Constant(gate, doubleValue); break; } case OpCode::ZEXT_INT1_TO_INT32: { @@ -395,6 +396,14 @@ void LLVMIRBuilder::Build() } case OpCode::DEPEND_AND: break; + case OpCode::INT32_SMOD: { + VisitIntMod(gate, ins[0], ins[1]); + break; + } + case OpCode::FLOAT64_SMOD: { + VisitFloatMod(gate, ins[0], ins[1]); + break; + } default: { LOG_ECMA(ERROR) << "The gate below need to be translated "; circuit_->Print(gate); @@ -750,7 +759,7 @@ void LLVMIRBuilder::VisitInt64Constant(AddrShift gate, int64_t value) const LOG_ECMA(INFO) << "VisitInt64Constant " << str; } -void LLVMIRBuilder::VisitFloat64Constant(AddrShift gate, int64_t value) const +void LLVMIRBuilder::VisitFloat64Constant(AddrShift gate, double value) const { LLVMValueRef llvmValue = LLVMConstReal(LLVMDoubleType(), value); LLVMTFBuilderBasicBlockImpl *impl = currentBb_->GetImpl(); @@ -977,6 +986,30 @@ void LLVMIRBuilder::VisitIntMul(AddrShift gate, AddrShift e1, AddrShift e2) cons LOG_ECMA(INFO) << "result: " << LLVMPrintValueToString(result); } +void LLVMIRBuilder::VisitIntMod(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + LOG_ECMA(INFO) << "int mod gate:" << gate; + LLVMValueRef e1Value = g_values[e1]; + LOG_ECMA(INFO) << "operand 0: " << LLVMPrintValueToString(e1Value); + LLVMValueRef e2Value = g_values[e2]; + LOG_ECMA(INFO) << "operand 1: " << LLVMPrintValueToString(e2Value); + LLVMValueRef result = LLVMBuildSRem(builder_, e1Value, e2Value, ""); + g_values[gate] = result; + LOG_ECMA(INFO) << "result: " << LLVMPrintValueToString(result); +} + +void LLVMIRBuilder::VisitFloatMod(AddrShift gate, AddrShift e1, AddrShift e2) const +{ + LOG_ECMA(INFO) << "float mod gate:" << gate; + LLVMValueRef e1Value = g_values[e1]; + LOG_ECMA(INFO) << "operand 0: " << LLVMPrintValueToString(e1Value); + LLVMValueRef e2Value = g_values[e2]; + LOG_ECMA(INFO) << "operand 1: " << LLVMPrintValueToString(e2Value); + LLVMValueRef result = LLVMBuildFRem(builder_, e1Value, e2Value, ""); + g_values[gate] = result; + LOG_ECMA(INFO) << "result: " << LLVMPrintValueToString(result); +} + void LLVMIRBuilder::VisitIntOr(AddrShift gate, AddrShift e1, AddrShift e2) const { LOG_ECMA(INFO) << "int or gate:" << gate; diff --git a/ecmascript/compiler/llvm_ir_builder.h b/ecmascript/compiler/llvm_ir_builder.h index bab53fd7a014335b425f6cec918a44048ab3e450..cd703fd7e3fac8cbf432c7787c183e803a7fddcb 100644 --- a/ecmascript/compiler/llvm_ir_builder.h +++ b/ecmascript/compiler/llvm_ir_builder.h @@ -156,7 +156,7 @@ private: void VisitParameter(AddrShift gate) const; void VisitInt32Constant(AddrShift gate, int32_t value) const; void VisitInt64Constant(AddrShift gate, int64_t value) const; - void VisitFloat64Constant(AddrShift gate, int64_t value) const; + void VisitFloat64Constant(AddrShift gate, double value) const; void VisitZExtInt(AddrShift gate, AddrShift e1, MachineRep rep) const; void VisitSExtInt(AddrShift gate, AddrShift e1, MachineRep rep) const; void VisitLoad(AddrShift gate, MachineRep rep, AddrShift base) const; @@ -187,6 +187,8 @@ private: void VisitCastInt64ToDouble(AddrShift gate, AddrShift e1) const; void VisitCastDoubleToInt(AddrShift gate, AddrShift e1) const; void VisitCastInt64ToPointer(AddrShift gate, AddrShift e1) const; + void VisitIntMod(AddrShift gate, AddrShift e1, AddrShift e2) const; + void VisitFloatMod(AddrShift gate, AddrShift e1, AddrShift e2) const; BasicBlock *EnsurBasicBlock(int id); LLVMValueRef LLVMCallingFp(LLVMModuleRef &module, LLVMBuilderRef &builder); diff --git a/ecmascript/compiler/stub.h b/ecmascript/compiler/stub.h index 8e8722c6aa3eaead953215cf9bec9959357613ef..1260e34bb681ab99c7c9d6aabc655fec551dde4d 100644 --- a/ecmascript/compiler/stub.h +++ b/ecmascript/compiler/stub.h @@ -17,6 +17,7 @@ #define ECMASCRIPT_COMPILER_Stub_H #include "ecmascript/accessor_data.h" +#include "ecmascript/base/number_helper.h" #include "ecmascript/compiler/circuit.h" #include "ecmascript/compiler/circuit_builder.h" #include "ecmascript/compiler/gate.h" @@ -27,6 +28,8 @@ #include "ecmascript/tagged_dictionary.h" namespace kungfu { +using namespace panda::ecmascript; + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DEFVARIABLE(varname, type, val) Stub::Variable varname(GetEnvironment(), type, NextVariableId(), val) @@ -693,8 +696,22 @@ public: AddrShift Int32Div(AddrShift x, AddrShift y); AddrShift Int64Div(AddrShift x, AddrShift y); + AddrShift Int32Mod(AddrShift x, AddrShift y) + { + return env_.GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_SMOD), x, y); + } + + AddrShift DoubleMod(AddrShift x, AddrShift y) + { + return env_.GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::FLOAT64_SMOD), x, y); + } + // bit operation - AddrShift Word32Or(AddrShift x, AddrShift y); + AddrShift Word32Or(AddrShift x, AddrShift y) + { + return env_.GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_OR), x, y); + } + AddrShift Word32And(AddrShift x, AddrShift y) { return env_.GetCircuitBuilder().NewArithMeticGate(OpCode(OpCode::INT32_AND), x, y); @@ -815,7 +832,17 @@ public: AddrShift DoubleIsNAN(AddrShift x) { AddrShift diff = DoubleEqual(x, x); - return Word32NotEqual(SExtInt1ToInt32(diff), GetInteger32Constant(0)); + return Word32Equal(SExtInt1ToInt32(diff), GetInteger32Constant(0)); + } + + AddrShift DoubleIsINF(AddrShift x) + { + AddrShift infinity = GetDoubleConstant(base::POSITIVE_INFINITY); + AddrShift negativeInfinity = GetDoubleConstant(-base::POSITIVE_INFINITY); + AddrShift diff1 = DoubleEqual(x, infinity); + AddrShift diff2 = DoubleEqual(x, negativeInfinity); + return Word32Or(Word32Equal(SExtInt1ToInt32(diff1), GetInteger32Constant(1)), + Word32Equal(SExtInt1ToInt32(diff2), GetInteger32Constant(1))); } AddrShift IntBuildTagged(AddrShift x) diff --git a/ecmascript/compiler/tests/stub_tests.cpp b/ecmascript/compiler/tests/stub_tests.cpp index 80ad747f63093ae107d54873a45f1b7bae0d287f..c684a94da8171bc183ae22d099ac1600159fd5d8 100644 --- a/ecmascript/compiler/tests/stub_tests.cpp +++ b/ecmascript/compiler/tests/stub_tests.cpp @@ -506,11 +506,33 @@ HWTEST_F_L0(StubTest, FastDivTest) assembler.Run(); auto engine = assembler.GetEngine(); auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + + // test normal Division operation uint64_t x1 = JSTaggedValue(50).GetRawData(); - uint64_t x2 = JSTaggedValue(25).GetRawData(); - std::cout << "x1 = " << x1 << " x2 = " << x2 << std::endl; - auto res = fn(x1, x2); - std::cout << "res for FastDiv(50, 25) = " << res.GetRawData() << std::endl; + uint64_t y1 = JSTaggedValue(25).GetRawData(); + std::cout << "x1 = " << x1 << " y1 = " << y1 << std::endl; + auto res1 = fn(x1, y1); + std::cout << "res for FastDiv(50, 25) = " << res1.GetRawData() << std::endl; + auto expectedG1 = FastRuntimeStub::FastDiv(JSTaggedValue(x1), JSTaggedValue(y1)); + EXPECT_EQ(res1, expectedG1); + + // test x == 0.0 or std::isnan(x) + uint64_t x2 = JSTaggedValue(base::NAN_VALUE).GetRawData(); + uint64_t y2 = JSTaggedValue(0).GetRawData(); + std::cout << "x2 = " << x1 << " y2 = " << y2 << std::endl; + auto res2 = fn(x2, y2); + std::cout << "res for FastDiv(base::NAN_VALUE, 0) = " << res2.GetRawData() << std::endl; + auto expectedG2 = FastRuntimeStub::FastDiv(JSTaggedValue(x2), JSTaggedValue(y2)); + EXPECT_EQ(res2, expectedG2); + + // test other + uint64_t x3 = JSTaggedValue(7).GetRawData(); + uint64_t y3 = JSTaggedValue(0).GetRawData(); + std::cout << "x2 = " << x3 << " y2 = " << y3 << std::endl; + auto res3 = fn(x3, y3); + std::cout << "res for FastDiv(7, 0) = " << res3.GetRawData() << std::endl; + auto expectedG3 = FastRuntimeStub::FastDiv(JSTaggedValue(x3), JSTaggedValue(y3)); + EXPECT_EQ(res3, expectedG3); } HWTEST_F_L0(StubTest, FastFindOwnElementStub) @@ -1224,4 +1246,76 @@ HWTEST_F_L0(StubTest, FastGetPropertyByNameStub) resVal = getPropertyByNamePtr(thread, obj.GetTaggedValue().GetRawData(), strBig.GetTaggedValue().GetRawData()); EXPECT_EQ(resVal.GetNumber(), y); } + +HWTEST_F_L0(StubTest, FastModTest) +{ + LLVMModuleRef module = LLVMModuleCreateWithName("fast_mod_module"); + LLVMSetTarget(module, "x86_64-unknown-linux-gnu"); + LLVMTypeRef paramTys[] = { + LLVMInt64Type(), + LLVMInt64Type(), + }; + LLVMValueRef function = LLVMAddFunction(module, "FastMod", LLVMFunctionType(LLVMInt64Type(), paramTys, 2, 0)); + Circuit netOfGates; + FastModStub optimizer(&netOfGates); + optimizer.GenerateCircuit(); + netOfGates.PrintAllGates(); + auto cfg = Scheduler::Run(&netOfGates); + for (size_t bbIdx = 0; bbIdx < cfg.size(); bbIdx++) { + std::cout << (netOfGates.GetOpCode(cfg[bbIdx].front()).IsCFGMerge() ? "MERGE_" : "BB_") << bbIdx << ":" + << std::endl; + for (size_t instIdex = cfg[bbIdx].size(); instIdex < 0; instIdex--) { + netOfGates.Print(cfg[bbIdx][instIdex - 1]); + } + } + LLVMIRBuilder llvmBuilder(&cfg, &netOfGates, module, function); + llvmBuilder.Build(); + LLVMAssembler assembler(module); + assembler.Run(); + auto engine = assembler.GetEngine(); + auto fn = reinterpret_cast(LLVMGetPointerToGlobal(engine, function)); + + // test left, right are all integer + int x = 7; + int y = 3; + auto result = fn(JSTaggedValue(x).GetRawData(), JSTaggedValue(y).GetRawData()); + JSTaggedValue expectRes = FastRuntimeStub::FastMod(JSTaggedValue(x), JSTaggedValue(y)); + EXPECT_EQ(result, expectRes); + + // test y == 0.0 || std::isnan(y) || std::isnan(x) || std::isinf(x) return NAN_VALUE + double x2 = 7.3; + int y2 = base::NAN_VALUE; + auto result2 = fn(JSTaggedValue(x2).GetRawData(), JSTaggedValue(y2).GetRawData()); + auto expectRes2 = FastRuntimeStub::FastMod(JSTaggedValue(x2), JSTaggedValue(y2)); + EXPECT_EQ(result2, expectRes2); + std::cout << "result2 for FastMod(7, 'helloworld') = " << result2.GetRawData() << std::endl; + std::cout << "expectRes2 for FastMod(7, 'helloworld') = " << expectRes2.GetRawData() << std::endl; + + // // test modular operation under normal conditions + double x3 = 33.0; + double y3 = 44.0; + auto result3 = fn(JSTaggedValue(x3).GetRawData(), JSTaggedValue(y3).GetRawData()); + auto expectRes3 = FastRuntimeStub::FastMod(JSTaggedValue(x3), JSTaggedValue(y3)); + EXPECT_EQ(result3, expectRes3); + + // test x == 0.0 || std::isinf(y) return x + double x4 = base::NAN_VALUE; + int y4 = 7; + auto result4 = fn(JSTaggedValue(x4).GetRawData(), JSTaggedValue(y4).GetRawData()); + auto expectRes4 = FastRuntimeStub::FastMod(JSTaggedValue(x4), JSTaggedValue(y4)); + + std::cout << "result4 for FastMod(base::NAN_VALUE, 7) = " << result4.GetRawData() << std::endl; + std::cout << "expectRes4 for FastMod(base::NAN_VALUE, 7) = " << expectRes4.GetRawData() << std::endl; + EXPECT_EQ(result4, expectRes4); + + // test all non-conforming conditions + int x5 = 7; + auto *factory = JSThread::Cast(thread)->GetEcmaVM()->GetFactory(); + auto y5 = factory->NewFromStdString("hello world"); + auto result5 = fn(JSTaggedValue(x5).GetRawData(), y5.GetTaggedValue().GetRawData()); + EXPECT_EQ(result5, JSTaggedValue::Hole()); + auto expectRes5 = FastRuntimeStub::FastMod(JSTaggedValue(x5), y5.GetTaggedValue()); + std::cout << "result1 for FastMod(7, 'helloworld') = " << result5.GetRawData() << std::endl; + EXPECT_EQ(result5, expectRes5); +} } // namespace panda::test