From 9e52ecdc90c4ec83fef109e49f6bbca100d3ba9e Mon Sep 17 00:00:00 2001 From: Yong Zhou Date: Thu, 21 Sep 2023 21:20:55 +0800 Subject: [PATCH] GlobalValueNumering implementation Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I83EHP?from=project-issue Change-Id: I471ed10b269c6b810d990acc6389878f7b418a8e Signed-off-by: Yong Zhou --- ecmascript/compiler/gate_accessor.cpp | 8 + ecmascript/compiler/gate_accessor.h | 1 + ecmascript/compiler/hcr_gate_meta_data.h | 14 ++ ecmascript/compiler/mcr_gate_meta_data.h | 37 +++ ecmascript/compiler/pass.h | 6 +- ecmascript/compiler/pass_manager.cpp | 2 + ecmascript/compiler/share_gate_meta_data.h | 49 ++++ ecmascript/compiler/tests/BUILD.gn | 18 ++ .../tests/global_value_numbering_test.cpp | 218 ++++++++++++++++++ .../compiler/tests/meta_data_equal_test.cpp | 213 +++++++++++++++++ ecmascript/compiler/value_numbering.cpp | 176 ++++++++++---- ecmascript/compiler/value_numbering.h | 30 ++- ecmascript/js_runtime_options.cpp | 19 ++ ecmascript/js_runtime_options.h | 24 ++ .../global_value_numbering_ts/BUILD.gn | 18 ++ .../expect_output.txt | 21 ++ .../global_value_numbering_ts.ts | 97 ++++++++ 17 files changed, 900 insertions(+), 51 deletions(-) create mode 100644 ecmascript/compiler/tests/global_value_numbering_test.cpp create mode 100644 ecmascript/compiler/tests/meta_data_equal_test.cpp create mode 100644 test/aottest/global_value_numbering_ts/BUILD.gn create mode 100644 test/aottest/global_value_numbering_ts/expect_output.txt create mode 100644 test/aottest/global_value_numbering_ts/global_value_numbering_ts.ts diff --git a/ecmascript/compiler/gate_accessor.cpp b/ecmascript/compiler/gate_accessor.cpp index 6b6e600ce8..a18f93cd34 100644 --- a/ecmascript/compiler/gate_accessor.cpp +++ b/ecmascript/compiler/gate_accessor.cpp @@ -1537,6 +1537,14 @@ bool GateAccessor::MetaDataEqu(GateRef g1, GateRef g2) const return GetMetaData(g1) == GetMetaData(g2); } +bool GateAccessor::MetaDataValueEqu(GateRef g1, GateRef g2) const +{ + const GateMetaData *g1Meta = GetMetaData(g1); + const GateMetaData *g2Meta = GetMetaData(g2); + + return g1Meta->equal(*g2Meta); +} + bool GateAccessor::IsNop(GateRef g) const { return GetMetaData(g)->IsNop(); diff --git a/ecmascript/compiler/gate_accessor.h b/ecmascript/compiler/gate_accessor.h index ec71db587c..545e6aedbf 100644 --- a/ecmascript/compiler/gate_accessor.h +++ b/ecmascript/compiler/gate_accessor.h @@ -521,6 +521,7 @@ public: bool IsProlog(GateRef g) const; bool IsCFGMerge(GateRef g) const; bool MetaDataEqu(GateRef g1, GateRef g2) const; + bool MetaDataValueEqu(GateRef g1, GateRef g2) const; bool IsNop(GateRef g) const; bool IsRoot(GateRef g) const; bool HasOuts(GateRef gate) const; diff --git a/ecmascript/compiler/hcr_gate_meta_data.h b/ecmascript/compiler/hcr_gate_meta_data.h index a9786514e2..5f38dab714 100644 --- a/ecmascript/compiler/hcr_gate_meta_data.h +++ b/ecmascript/compiler/hcr_gate_meta_data.h @@ -40,6 +40,20 @@ public: SetKind(GateMetaData::Kind::JSBYTECODE); } + bool equal(const GateMetaData &other) const override + { + if (!GateMetaData::equal(other)) { + return false; + } + auto cast_other = static_cast(&other); + if (opcode_ == cast_other->opcode_ && + pcOffset_ == cast_other->pcOffset_ && type_ == cast_other->type_ && + elementsKinds_ == cast_other->elementsKinds_) { + return true; + } + return false; + } + static const JSBytecodeMetaData* Cast(const GateMetaData* meta) { meta->AssertKind(GateMetaData::Kind::JSBYTECODE); diff --git a/ecmascript/compiler/mcr_gate_meta_data.h b/ecmascript/compiler/mcr_gate_meta_data.h index 99007ee5d5..69d1f21657 100644 --- a/ecmascript/compiler/mcr_gate_meta_data.h +++ b/ecmascript/compiler/mcr_gate_meta_data.h @@ -173,6 +173,18 @@ public: SetKind(GateMetaData::Kind::TYPED_CALL); } + bool equal(const GateMetaData &other) const override + { + if (!OneParameterMetaData::equal(other)) { + return false; + } + auto cast_other = static_cast(&other); + if (noGC_ == cast_other->noGC_) { + return true; + } + return false; + } + static const TypedCallMetaData* Cast(const GateMetaData* meta) { meta->AssertKind(GateMetaData::Kind::TYPED_CALL); @@ -196,6 +208,19 @@ public: SetKind(GateMetaData::Kind::TYPED_CALLTARGETCHECK_OP); } + bool equal(const GateMetaData &other) const override + { + if (!OneParameterMetaData::equal(other)) { + return false; + } + auto cast_other = + static_cast(&other); + if (checkOp_ == cast_other->checkOp_) { + return true; + } + return false; + } + static const TypedCallTargetCheckMetaData* Cast(const GateMetaData* meta) { meta->AssertKind(GateMetaData::Kind::TYPED_CALLTARGETCHECK_OP); @@ -219,6 +244,18 @@ public: SetKind(GateMetaData::Kind::TYPED_BINARY_OP); } + bool equal(const GateMetaData &other) const override + { + if (!OneParameterMetaData::equal(other)) { + return false; + } + auto cast_other = static_cast(&other); + if (binOp_ == cast_other->binOp_ && type_ == cast_other->type_) { + return true; + } + return false; + } + static const TypedBinaryMetaData* Cast(const GateMetaData* meta) { meta->AssertKind(GateMetaData::Kind::TYPED_BINARY_OP); diff --git a/ecmascript/compiler/pass.h b/ecmascript/compiler/pass.h index 636d7e8752..e9f044432f 100644 --- a/ecmascript/compiler/pass.h +++ b/ecmascript/compiler/pass.h @@ -47,6 +47,7 @@ #include "ecmascript/compiler/type_mcr_lowering.h" #include "ecmascript/compiler/value_numbering.h" #include "ecmascript/compiler/verifier.h" +#include "ecmascript/js_runtime_options.h" namespace panda::ecmascript::kungfu { class PassContext; @@ -580,11 +581,14 @@ public: if (!passOptions->EnableTypeLowering() || !passOptions->EnableValueNumbering()) { return false; } + JSRuntimeOptions runtimeOption = data->GetPassContext()->GetEcmaVM()->GetJSOptions(); TimeScope timescope("ValueNumberingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); Chunk chunk(data->GetNativeAreaAllocator()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); - ValueNumbering valueNumbering(data->GetCircuit(), &visitor, &chunk); + ValueNumbering valueNumbering(data->GetCircuit(), &visitor, &chunk, + runtimeOption.IsEnableNewValueNumbering(), + runtimeOption.GetTraceValueNumbering()); visitor.AddPass(&valueNumbering); visitor.VisitGraph(); visitor.PrintLog("value numbering"); diff --git a/ecmascript/compiler/pass_manager.cpp b/ecmascript/compiler/pass_manager.cpp index daa5ae316b..194cc310ca 100644 --- a/ecmascript/compiler/pass_manager.cpp +++ b/ecmascript/compiler/pass_manager.cpp @@ -134,7 +134,9 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(); + pipeline.RunPass(); pipeline.RunPass(); + pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(); diff --git a/ecmascript/compiler/share_gate_meta_data.h b/ecmascript/compiler/share_gate_meta_data.h index eadb1fb692..96ec708cde 100644 --- a/ecmascript/compiler/share_gate_meta_data.h +++ b/ecmascript/compiler/share_gate_meta_data.h @@ -107,6 +107,15 @@ public: : opcode_(opcode), flags_(flags), statesIn_(statesIn), dependsIn_(dependsIn), valuesIn_(valuesIn) {} + virtual bool equal(const GateMetaData &other) const + { + if (opcode_ == other.opcode_ && kind_ == other.kind_ && flags_ == other.flags_ && + statesIn_ == other.statesIn_ && dependsIn_ == other.dependsIn_ && valuesIn_ == other.valuesIn_) { + return true; + } + return false; + } + size_t GetStateCount() const { return statesIn_; @@ -282,6 +291,18 @@ public: SetKind(GateMetaData::Kind::IMMUTABLE_BOOL); } + bool equal(const GateMetaData &other) const override + { + if (!GateMetaData::equal(other)) { + return false; + } + auto cast_other = static_cast(&other); + if (value_ == cast_other->value_) { + return true; + } + return false; + } + static const BoolMetaData* Cast(const GateMetaData* meta) { meta->AssertKind(GateMetaData::Kind::IMMUTABLE_BOOL); @@ -306,6 +327,18 @@ public: SetKind(GateMetaData::Kind::IMMUTABLE_ONE_PARAMETER); } + bool equal(const GateMetaData &other) const override + { + if (!GateMetaData::equal(other)) { + return false; + } + auto cast_other = static_cast(&other); + if (value_ == cast_other->value_) { + return true; + } + return false; + } + static const OneParameterMetaData* Cast(const GateMetaData* meta) { ASSERT(meta->IsOneParameterKind()); @@ -341,6 +374,22 @@ public: } SetKind(GateMetaData::Kind::MUTABLE_STRING); } + bool equal(const GateMetaData &other) const override + { + if (!GateMetaData::equal(other)) { + return false; + } + auto cast_other = static_cast(&other); + if (stringData_.size() != cast_other->GetString().size()) { + return false; + } + + if (strncmp(stringData_.data(), cast_other->GetString().data(), stringData_.size()) != 0) { + return false; + } + + return true; + } const ChunkVector &GetString() const { diff --git a/ecmascript/compiler/tests/BUILD.gn b/ecmascript/compiler/tests/BUILD.gn index 6d3d4f7d56..4510153f1c 100644 --- a/ecmascript/compiler/tests/BUILD.gn +++ b/ecmascript/compiler/tests/BUILD.gn @@ -101,12 +101,30 @@ host_unittest_action("LoopOptimizationTest") { ] } +host_unittest_action("GlobalValueNumberingTest") { + module_out_path = module_output_path + + sources = [ + # test file + "global_value_numbering_test.cpp", + "meta_data_equal_test.cpp", + ] + + deps = [ + "$ark_root/libpandafile:libarkfile_static", + "$js_root:libark_jsruntime_test_set", + "$js_root/ecmascript/compiler:libark_jsoptimizer_set", + sdk_libc_secshared_dep, + ] +} + group("host_unittest") { testonly = true # deps file deps = [ ":AssemblerTestAction", + ":GlobalValueNumberingTestAction", ":LoopOptimizationTestAction", ":TypedArrayLoweringTestAction", ] diff --git a/ecmascript/compiler/tests/global_value_numbering_test.cpp b/ecmascript/compiler/tests/global_value_numbering_test.cpp new file mode 100644 index 0000000000..62f772368f --- /dev/null +++ b/ecmascript/compiler/tests/global_value_numbering_test.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/compiler/pass.h" +#include "ecmascript/compiler/share_gate_meta_data.h" +#include "ecmascript/compiler/share_opcodes.h" +#include "ecmascript/compiler/value_numbering.h" +#include "ecmascript/compiler/verifier.h" +#include "ecmascript/compiler/ts_hcr_lowering.h" +#include "ecmascript/compiler/type_mcr_lowering.h" +#include "ecmascript/elements.h" +#include "ecmascript/mem/concurrent_marker.h" +#include "ecmascript/mem/native_area_allocator.h" +#include "ecmascript/tests/test_helper.h" +#include "gtest/gtest.h" +#include +#include + +namespace panda::test { +class GlobalValueNumberingTests : public testing::Test {}; + +using ecmascript::kungfu::Circuit; +using ecmascript::kungfu::CircuitBuilder; +using ecmascript::kungfu::CombinedPassVisitor; +using ecmascript::kungfu::EcmaOpcode; +using ecmascript::kungfu::Environment; +using ecmascript::kungfu::GateAccessor; +using ecmascript::kungfu::GateRef; +using ecmascript::kungfu::PGOSampleType; +using ecmascript::kungfu::ValueNumbering; +using ecmascript::kungfu::Verifier; + + +HWTEST_F_L0(GlobalValueNumberingTests, AllInputsCheckedTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + auto x = builder.Arguments(1); + auto y = builder.Arguments(2); + auto z = builder.Arguments(3); + auto add1 = builder.Int64Add(x, y); + auto add2 = builder.Int64Add(x, y); + auto add3 = builder.Int64Add(x, z); + + CombinedPassVisitor visitor(&circuit, false, "ValueNumbering", &chunk); + ValueNumbering valuenumber(&circuit, &visitor, &chunk, true, false); + + EXPECT_EQ(valuenumber.VisitGate(add1), Circuit::NullGate()); + EXPECT_EQ(valuenumber.VisitGate(add2), add1); + EXPECT_EQ(valuenumber.VisitGate(add3), Circuit::NullGate()); +} + + +HWTEST_F_L0(GlobalValueNumberingTests, DeadNodesTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + auto x = builder.Arguments(1); + auto y = builder.Arguments(2); + + auto add1 = builder.Int64Add(x, y); + auto add2 = builder.Int64Add(x, y); + + CombinedPassVisitor visitor(&circuit, false, "ValueNumbering", &chunk); + ValueNumbering valuenumber(&circuit, &visitor, &chunk, true, false); + + EXPECT_EQ(valuenumber.VisitGate(add1), Circuit::NullGate()); + acc.DeleteGate(add1); + EXPECT_EQ(valuenumber.VisitGate(add2), Circuit::NullGate()); +} + +HWTEST_F_L0(GlobalValueNumberingTests, WontReplaceNodeWithItself) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + auto x = builder.Arguments(1); + auto y = builder.Arguments(2); + + auto add1 = builder.Int64Add(x, y); + + CombinedPassVisitor visitor(&circuit, false, "ValueNumbering", &chunk); + ValueNumbering valuenumber(&circuit, &visitor, &chunk, true, false); + + EXPECT_EQ(valuenumber.VisitGate(add1), Circuit::NullGate()); + EXPECT_EQ(valuenumber.VisitGate(add1), Circuit::NullGate()); +} + + +HWTEST_F_L0(GlobalValueNumberingTests, E2ESimpleAddTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + auto x = builder.Arguments(1); + auto y = builder.Arguments(2); + + + auto add1 = builder.Int64Add(x, y); + auto add2 = builder.Int64Add(x, y); + auto add3 = builder.Int64Add(add1, add2); + + builder.Return(add3); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "ValueNumbering", &chunk); + ValueNumbering valuenumber(&circuit, &visitor, &chunk, true, false); + visitor.AddPass(&valuenumber); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_TRUE(acc.GetValueIn(add3, 0) == acc.GetValueIn(add3, 1)); + EXPECT_EQ(valuenumber.GetoptimizedGateCount(), 1); +} + +HWTEST_F_L0(GlobalValueNumberingTests, GrowStressTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + std::vector results; + auto x = builder.Arguments(1); + auto y = builder.Arguments(2); + + for (int i = 0; i < 10000; i++) { + auto add1 = builder.Int64Add(x, y); + results.push_back(add1); + } + + GateRef before = results[0]; + for (int i = 1; i < 10000; i++) { + before = builder.Int64Add(before, results[i]); + } + + builder.Return(before); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "ValueNumbering", &chunk); + ValueNumbering valuenumber(&circuit, &visitor, &chunk, true, false); + visitor.AddPass(&valuenumber); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(valuenumber.GetoptimizedGateCount(), 9999); +} + + +HWTEST_F_L0(GlobalValueNumberingTests, ComplexAddTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + auto x = builder.Arguments(1); + auto y = builder.Arguments(2); + + auto add1 = builder.Int64Add(x, y); + auto add2 = builder.Int64Add(x, y); + auto add3 = builder.Int64Add(add1, add2); + + auto add4 = builder.Int64Add(x, y); + auto add5 = builder.Int64Add(x, y); + auto add6 = builder.Int64Add(add4, add5); + + auto add7 = builder.Int64Add(add3, add6); + builder.Return(add7); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "ValueNumbering", &chunk); + ValueNumbering valuenumber(&circuit, &visitor, &chunk, true, false); + visitor.AddPass(&valuenumber); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(valuenumber.GetoptimizedGateCount(), 4); +} + +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/compiler/tests/meta_data_equal_test.cpp b/ecmascript/compiler/tests/meta_data_equal_test.cpp new file mode 100644 index 0000000000..eed1b2257e --- /dev/null +++ b/ecmascript/compiler/tests/meta_data_equal_test.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/compiler/share_opcodes.h" +#include "ecmascript/compiler/verifier.h" +#include "ecmascript/compiler/ts_hcr_lowering.h" +#include "ecmascript/compiler/type_mcr_lowering.h" +#include "ecmascript/elements.h" +#include "ecmascript/mem/concurrent_marker.h" +#include "ecmascript/mem/native_area_allocator.h" +#include "ecmascript/tests/test_helper.h" +#include "gtest/gtest.h" +#include + +namespace panda::test { +class MetaDataEqualTests : public testing::Test {}; + +using ecmascript::kungfu::Circuit; +using ecmascript::kungfu::CircuitBuilder; +using ecmascript::kungfu::EcmaOpcode; +using ecmascript::kungfu::ElementsKind; +using ecmascript::kungfu::Environment; +using ecmascript::kungfu::GateAccessor; +using ecmascript::kungfu::GateMetaData; +using ecmascript::kungfu::GateType; +using ecmascript::kungfu::JSBytecodeMetaData; +using ecmascript::kungfu::MachineType; +using ecmascript::kungfu::PGOSampleType; +using ecmascript::kungfu::TypedBinOp; +using ecmascript::kungfu::TypedCallTargetCheckOp; + +HWTEST_F_L0(MetaDataEqualTests, StringMetaDataEqualTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + auto stringGate1 = circuit.GetConstantStringGate(MachineType::ARCH, "test1", GateType::NJSValue()); + + + auto stringGate2 = circuit.GetConstantStringGate(MachineType::ARCH, "test2", GateType::NJSValue()); + + EXPECT_FALSE(acc.MetaDataValueEqu(stringGate1, stringGate2)); + + auto stringGate3 = circuit.GetConstantStringGate(MachineType::ARCH, "test1", GateType::NJSValue()); + + EXPECT_TRUE(acc.MetaDataValueEqu(stringGate1, stringGate3)); +} + +HWTEST_F_L0(MetaDataEqualTests, ConstantMetaDataEqualTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + + auto constantValue1 = circuit.GetConstantGate(MachineType::I64, 2, GateType::NJSValue()); + + auto constantValue2 = circuit.GetConstantGate(MachineType::I64, 2, GateType::NJSValue()); + + EXPECT_TRUE(acc.MetaDataValueEqu(constantValue1, constantValue2)); + + auto constantValue3 = circuit.GetConstantGate(MachineType::I64, 2, GateType::NJSValue()); + + auto constantValue4 = circuit.GetConstantGate(MachineType::I64, 3, GateType::NJSValue()); + + EXPECT_FALSE(acc.MetaDataValueEqu(constantValue3, constantValue4)); + + + // MetaData is equal, but Gate is not equal + auto constantValue5 = circuit.GetConstantGate(MachineType::I64, 2, GateType::NJSValue()); + + auto constantValue6 = circuit.GetConstantGate(MachineType::I32, 2, GateType::NJSValue()); + + EXPECT_TRUE(acc.MetaDataValueEqu(constantValue5, constantValue6)); + + // MetaData is equal, but Gate is not equal + auto ConstGateNJSValue2 = circuit.GetConstantGate(MachineType::I64, 2, GateType::NJSValue()); + + auto ConstGateUndefined = + circuit.GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, GateType::UndefinedType()); + + EXPECT_TRUE(acc.MetaDataValueEqu(ConstGateNJSValue2, ConstGateUndefined)); +} + +HWTEST_F_L0(MetaDataEqualTests, TypeErrorMetaDataEqualTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + auto constantValue1 = circuit.GetConstantGate(MachineType::I64, 2, GateType::NJSValue()); + auto stringGate1 = circuit.GetConstantStringGate(MachineType::ARCH, "test1", GateType::NJSValue()); + EXPECT_FALSE(acc.MetaDataValueEqu(constantValue1, stringGate1)); + EXPECT_FALSE(acc.MetaDataValueEqu(stringGate1, constantValue1)); +} + +HWTEST_F_L0(MetaDataEqualTests, HCRMetaDataEqualTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + // JSBytecodeMetaData + auto meta = circuit.JSBytecode(0, EcmaOpcode::JEQZ_IMM8, 0, true, false); + auto gate = + circuit.NewGate(meta, MachineType::I64, {Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + auto meta2 = circuit.JSBytecode(0, EcmaOpcode::JEQZ_IMM8, 0, true, false); + auto gate2 = + circuit.NewGate(meta2, MachineType::I64, {Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + + EXPECT_TRUE(acc.MetaDataValueEqu(gate, gate2)); + + static_cast(const_cast(meta))->SetElementsKind(ElementsKind::NUMBER); + static_cast(const_cast(meta2))->SetElementsKind(ElementsKind::NUMBER); + EXPECT_TRUE(acc.MetaDataValueEqu(gate, gate2)); + + static_cast(const_cast(meta))->SetType(PGOSampleType::CreateProfileType(0, 0)); + static_cast(const_cast(meta2))->SetType(PGOSampleType::CreateProfileType(0, 0)); + EXPECT_TRUE(acc.MetaDataValueEqu(gate, gate2)); + + + static_cast(const_cast(meta))->SetType(PGOSampleType::CreateProfileType(0, 0)); + static_cast(const_cast(meta2))->SetType(PGOSampleType::CreateProfileType(0, 1)); + EXPECT_FALSE(acc.MetaDataValueEqu(gate, gate2)); + + static_cast(const_cast(meta))->SetElementsKind(ElementsKind::NUMBER); + static_cast(const_cast(meta2))->SetElementsKind(ElementsKind::HOLE_NUMBER); + EXPECT_FALSE(acc.MetaDataValueEqu(gate, gate2)); +} + +HWTEST_F_L0(MetaDataEqualTests, MCRMetaDataEqualTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + + // TypedCallMetaData + auto callGate = + circuit.NewGate(circuit.TypedCall(0, 0, true), MachineType::I64, + {Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + + auto callGate2 = + circuit.NewGate(circuit.TypedCall(0, 0, true), MachineType::I64, + {Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + + EXPECT_TRUE(acc.MetaDataValueEqu(callGate, callGate2)); + EXPECT_TRUE(acc.MetaDataValueEqu(callGate2, callGate)); + + // TypedCallTargetCheckMetaData + auto callGate3 = + circuit.NewGate(circuit.TypedCallTargetCheckOp(0, 0, TypedCallTargetCheckOp::JSCALLTHIS_FAST), MachineType::I64, + {Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + auto callGate4 = + circuit.NewGate(circuit.TypedCallTargetCheckOp(0, 0, TypedCallTargetCheckOp::JSCALLTHIS_FAST), MachineType::I64, + {Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + + EXPECT_TRUE(acc.MetaDataValueEqu(callGate3, callGate4)); + EXPECT_TRUE(acc.MetaDataValueEqu(callGate4, callGate3)); + + // TypedBinaryMetaData + auto callGate5 = circuit.NewGate( + circuit.TypedBinaryOp(0, TypedBinOp::TYPED_ADD, PGOSampleType::CreateProfileType(0, 1)), MachineType::I64, + {Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + + // TypedBinaryMetaData + auto callGate6 = circuit.NewGate( + circuit.TypedBinaryOp(0, TypedBinOp::TYPED_ADD, PGOSampleType::CreateProfileType(0, 1)), MachineType::I64, + {Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate(), Circuit::NullGate()}, GateType::AnyType()); + + EXPECT_TRUE(acc.MetaDataValueEqu(callGate5, callGate6)); + EXPECT_TRUE(acc.MetaDataValueEqu(callGate6, callGate5)); +} + + +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/compiler/value_numbering.cpp b/ecmascript/compiler/value_numbering.cpp index 2eee303cb4..9409ba63b3 100644 --- a/ecmascript/compiler/value_numbering.cpp +++ b/ecmascript/compiler/value_numbering.cpp @@ -13,71 +13,169 @@ * limitations under the License. */ #include "ecmascript/compiler/value_numbering.h" - +#include + namespace panda::ecmascript::kungfu { GateRef ValueNumbering::VisitGate(GateRef gate) { - auto opcode = acc_.GetOpCode(gate); - if (opcode != OpCode::CONVERT) { - return Circuit::NullGate(); - } size_t hash = HashCode(gate); - if (entries_.size() == 0) { - entries_.resize(CACHE_LENGTH, Circuit::NullGate()); - SetEntry(hash, gate); + auto opcode = acc_.GetOpCode(gate); + if (opcode != OpCode::CONVERT && !useNewGVN_) { return Circuit::NullGate(); } - GateRef replacement = GetEntry(hash); - if (replacement != Circuit::NullGate() && - CheckReplacement(gate, replacement)) { - return replacement; + if (entries_ == nullptr) { + InitEntries(entriesLength_); + SetEntry(hash, gate); + entriesSize_++; + return Circuit::NullGate(); + } + ASSERT(entriesSize_ + entriesSize_ / LOAD_FACTOR_THRESHOLD < entriesLength_); + const size_t mask = entriesLength_ - 1; + size_t dead = entriesLength_; + for (size_t i = hash & mask;; i = (i + 1) & mask) { + GateRef entry = entries_[i]; + if (entry == Circuit::NullGate()) { + if (dead != entriesLength_) { + entries_[dead] = gate; + } else { + // Have to insert a new entry. + entries_[i] = gate; + entriesSize_++; + // Resize to keep load factor below 80% + EnsureCapacity(); + } + ASSERT(entriesSize_ + entriesSize_ / LOAD_FACTOR_THRESHOLD < entriesLength_); + return Circuit::NullGate(); + } + + if (entry == gate) { + return Circuit::NullGate(); + } + + // Skip dead entries, but remember their indices so we can reuse them. + if (acc_.IsNop(entry)) { + dead = i; + continue; + } + + if (CheckReplacement(gate, entry)) { + optimizedGateCount++; + if (enableLog_) { + LOG_COMPILER(INFO) << "Found a replaceable node, before -> after"; + acc_.Print(gate); + acc_.Print(entry); + } + return entry; + } } - SetEntry(hash, gate); return Circuit::NullGate(); } +void ValueNumbering::EnsureCapacity() +{ + if (entriesSize_ + entriesSize_ / LOAD_FACTOR_THRESHOLD >= entriesLength_) { + Grow(); + } +} + +void ValueNumbering::Grow() +{ + GateRef *const oldEntries = entries_; + size_t const oldSize = entriesLength_; + entriesLength_ *= 2; + InitEntries(entriesLength_); + size_t const mask = entriesLength_ - 1; + + for (size_t i = 0; i < oldSize; i++) { + GateRef oldEnrty = oldEntries[i]; + if (oldEnrty == Circuit::NullGate() || acc_.IsNop(oldEnrty)) { + continue; + } + for (size_t j = HashCode(oldEnrty) & mask;; j = (j + 1) & mask) { + GateRef entry = entries_[j]; + if (entry == oldEnrty) { + break; + } + if (entry == Circuit::NullGate()) { + entries_[j] = oldEnrty; + entriesSize_++; + break; + } + } + } + chunk_->Free(oldEntries); + if (enableLog_) { + LOG_COMPILER(INFO) << "Grow happend"; + } +} + +void ValueNumbering::InitEntries(size_t initSize) +{ + entries_ = chunk_->NewArray(initSize); + for (size_t i = 0; i < initSize; i++) { + entries_[i] = Circuit::NullGate(); + } + entriesSize_ = 0; +} + + +size_t HashCombine(size_t seed, size_t value) +{ + // In the meantime, we're not considering 32-bit systems + assert(sizeof(void *) == 8); + const uint64_t m = uint64_t{0xC6A4A7935BD1E995}; + const uint32_t r = 47; + + value *= m; + value ^= value >> r; + value *= m; + + seed ^= value; + seed *= m; + return seed; +} + size_t ValueNumbering::HashCode(GateRef gate) { - size_t hash = static_cast(acc_.GetOpCode(gate)); - hash ^= acc_.TryGetValue(gate); size_t valueCount = acc_.GetNumValueIn(gate); + size_t hash = HashCombine(static_cast(acc_.GetOpCode(gate)), valueCount); for (size_t i = 0; i < valueCount; i++) { - GateRef input = acc_.GetValueIn(gate); + GateRef input = acc_.GetValueIn(gate, i); auto id = acc_.GetId(input); - hash ^= id; + hash = HashCombine(hash, id); } - return hash % CACHE_LENGTH; + return hash; } +// Gates are considered replaceable only when their values are identical bool ValueNumbering::CheckReplacement(GateRef lhs, GateRef rhs) { - if (!acc_.MetaDataEqu(lhs, rhs)) { - if (acc_.GetOpCode(lhs) != acc_.GetOpCode(rhs)) { + if (acc_.GetOpCode(lhs) != acc_.GetOpCode(rhs)) + return false; + + size_t valueCount1 = acc_.GetNumIns(lhs); + size_t valueCount2 = acc_.GetNumIns(rhs); + if (valueCount1 != valueCount2) + return false; + for (size_t i = 0; i < valueCount1; i++) { + if (acc_.GetIn(lhs, i) != acc_.GetIn(rhs, i)) { return false; } } - size_t valueCount = acc_.GetNumValueIn(lhs); - for (size_t i = 0; i < valueCount; i++) { - if (acc_.GetValueIn(lhs, i) != acc_.GetValueIn(rhs, i)) { - return false; - } + + if (acc_.GetGateType(lhs) != acc_.GetGateType(rhs)) { + return false; } - if (acc_.HasFrameState(lhs)) { - ASSERT(acc_.HasFrameState(rhs)); - if (acc_.GetFrameState(lhs) != acc_.GetFrameState(rhs)) { - return false; - } + + if (acc_.GetMachineType(lhs) != acc_.GetMachineType(rhs)) { + return false; } - auto opcode = acc_.GetOpCode(lhs); - if (opcode == OpCode::CONVERT) { - if (acc_.GetSrcType(lhs) != acc_.GetSrcType(rhs)) { - return false; - } - if (acc_.GetDstType(lhs) != acc_.GetDstType(rhs)) { - return false; - } + + if (!acc_.MetaDataValueEqu(lhs, rhs)) { + return false; } + return true; } -} // namespace panda::ecmascript::kungfu \ No newline at end of file +} // namespace panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/value_numbering.h b/ecmascript/compiler/value_numbering.h index 949ab1c127..c3e9d1add5 100644 --- a/ecmascript/compiler/value_numbering.h +++ b/ecmascript/compiler/value_numbering.h @@ -24,29 +24,37 @@ namespace panda::ecmascript::kungfu { class ValueNumbering : public PassVisitor { public: - ValueNumbering(Circuit *circuit, RPOVisitor *visitor, Chunk* chunk) - : PassVisitor(circuit, chunk, visitor), entries_(chunk) {} + ValueNumbering(Circuit *circuit, RPOVisitor *visitor, Chunk* chunk, bool useNewGVN, bool enableLog) + : PassVisitor(circuit, chunk, visitor), entries_(nullptr), useNewGVN_(useNewGVN), + enableLog_(enableLog) {} ~ValueNumbering() = default; GateRef VisitGate(GateRef gate) override; bool CheckReplacement(GateRef lhs, GateRef rhs); -private: - size_t HashCode(GateRef gate); - GateRef GetEntry(size_t hash) + int GetoptimizedGateCount() { - ASSERT(hash < entries_.size()); - return entries_[hash]; + return optimizedGateCount; } + +private: + void Grow(); + void EnsureCapacity(); + void InitEntries(size_t initSize); + size_t HashCode(GateRef gate); void SetEntry(size_t hash, GateRef gate) { - ASSERT(hash < entries_.size()); - entries_[hash] = gate; + entries_[hash & (entriesLength_ - 1)] = gate; } static const uint32_t CACHE_LENGTH_BIT = 8; static const uint32_t CACHE_LENGTH = (1U << CACHE_LENGTH_BIT); - - ChunkVector entries_; + const uint8_t LOAD_FACTOR_THRESHOLD = 4; + uint32_t entriesLength_ = (1U << CACHE_LENGTH_BIT); + uint32_t entriesSize_ = 0; + GateRef* entries_; + bool useNewGVN_; + int optimizedGateCount = 0; + bool enableLog_ = false; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_VALUE_NUMBERING_H \ No newline at end of file diff --git a/ecmascript/js_runtime_options.cpp b/ecmascript/js_runtime_options.cpp index 7f09c22a4e..633f33bf4a 100644 --- a/ecmascript/js_runtime_options.cpp +++ b/ecmascript/js_runtime_options.cpp @@ -69,6 +69,7 @@ const std::string PUBLIC_API HELP_OPTION_MSG = "--compiler-trace-bc: Enable tracing bytecode for aot runtime. Default: 'false'\n" "--compiler-trace-deopt: Enable tracing deopt for aot runtime. Default: 'false'\n" "--compiler-trace-inline: Enable tracing inline function for aot runtime. Default: 'false'\n" + "--compiler-trace-value-numbering: Enable tracing value numbering for aot runtime. Default: 'false'\n" "--compiler-max-inline-bytecodes Set max bytecodes count which aot function can be inlined. Default: '25'\n" "--compiler-deopt-threshold: Set max count which aot function can occur deoptimization. Default: '10'\n" "--compiler-stress-deopt: Enable stress deopt for aot compiler. Default: 'false'\n" @@ -172,6 +173,7 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) {"compiler-trace-bc", required_argument, nullptr, OPTION_COMPILER_TRACE_BC}, {"compiler-trace-deopt", required_argument, nullptr, OPTION_COMPILER_TRACE_DEOPT}, {"compiler-trace-inline", required_argument, nullptr, OPTION_COMPILER_TRACE_INLINE}, + {"compiler-trace-value-numbering", required_argument, nullptr, OPTION_COMPILER_TRACE_VALUE_NUMBERING}, {"compiler-max-inline-bytecodes", required_argument, nullptr, OPTION_COMPILER_MAX_INLINE_BYTECODES}, {"compiler-deopt-threshold", required_argument, nullptr, OPTION_COMPILER_DEOPT_THRESHOLD}, {"compiler-stress-deopt", required_argument, nullptr, OPTION_COMPILER_STRESS_DEOPT}, @@ -185,6 +187,7 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) {"compiler-opt-early-elimination", required_argument, nullptr, OPTION_COMPILER_OPT_EARLY_ELIMINATION}, {"compiler-opt-later-elimination", required_argument, nullptr, OPTION_COMPILER_OPT_LATER_ELIMINATION}, {"compiler-opt-value-numbering", required_argument, nullptr, OPTION_COMPILER_OPT_VALUE_NUMBERING}, + {"compiler-opt-new-value-numbering", required_argument, nullptr, OPTION_COMPILER_OPT_NEW_VALUE_NUMBERING}, {"compiler-opt-inlining", required_argument, nullptr, OPTION_COMPILER_OPT_INLINING}, {"compiler-opt-pgotype", required_argument, nullptr, OPTION_COMPILER_OPT_PGOTYPE}, {"compiler-opt-track-field", required_argument, nullptr, OPTION_COMPILER_OPT_TRACK_FIELD}, @@ -371,6 +374,14 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) return false; } break; + case OPTION_COMPILER_TRACE_VALUE_NUMBERING: + ret = ParseBoolParam(&argBool); + if (ret) { + SetTraceValueNumbering(argBool); + } else { + return false; + } + break; case OPTION_COMPILER_MAX_INLINE_BYTECODES: ret = ParseUint32Param("max-inline-bytecodes", &argUint32); if (ret) { @@ -668,6 +679,14 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) return false; } break; + case OPTION_COMPILER_OPT_NEW_VALUE_NUMBERING: + ret = ParseBoolParam(&argBool); + if (ret) { + SetEnableNewValueNumbering(argBool); + } else { + return false; + } + break; case OPTION_COMPILER_OPT_INLINING: ret = ParseBoolParam(&argBool); if (ret) { diff --git a/ecmascript/js_runtime_options.h b/ecmascript/js_runtime_options.h index 33f4b09399..0961ec196c 100644 --- a/ecmascript/js_runtime_options.h +++ b/ecmascript/js_runtime_options.h @@ -98,6 +98,7 @@ enum CommandValues { OPTION_COMPILER_TRACE_BC, OPTION_COMPILER_TRACE_DEOPT, OPTION_COMPILER_TRACE_INLINE, + OPTION_COMPILER_TRACE_VALUE_NUMBERING, OPTION_COMPILER_MAX_INLINE_BYTECODES, OPTION_COMPILER_DEOPT_THRESHOLD, OPTION_COMPILER_STRESS_DEOPT, @@ -117,6 +118,7 @@ enum CommandValues { OPTION_COMPILER_OPT_EARLY_ELIMINATION, OPTION_COMPILER_OPT_LATER_ELIMINATION, OPTION_COMPILER_OPT_VALUE_NUMBERING, + OPTION_COMPILER_OPT_NEW_VALUE_NUMBERING, OPTION_COMPILER_OPT_INLINING, OPTION_COMPILER_OPT_PGOTYPE, OPTION_COMPILER_OPT_TRACK_FIELD, @@ -981,6 +983,16 @@ public: return enableValueNumbering_; } + void SetEnableNewValueNumbering(bool value) + { + enableNewValueNumbering_ = value; + } + + bool IsEnableNewValueNumbering() const + { + return enableNewValueNumbering_; + } + void SetEnableOptInlining(bool value) { enableOptInlining_ = value; @@ -1116,6 +1128,16 @@ public: return traceInline_; } + void SetTraceValueNumbering(bool value) + { + traceValueNumbering_ = value; + } + + bool GetTraceValueNumbering() const + { + return traceValueNumbering_; + } + void SetMaxInlineBytecodes(size_t value) { maxInlineBytecodes_ = value; @@ -1304,6 +1326,7 @@ private: bool enableEarlyElimination_ {true}; bool enableLaterElimination_ {true}; bool enableValueNumbering_ {true}; + bool enableNewValueNumbering_ {true}; bool enableOptInlining_ {true}; bool enableOptPGOType_ {true}; bool enableGlobalTypeInfer_ {false}; @@ -1325,6 +1348,7 @@ private: std::string compilerSelectMethods_ {""}; std::string compilerSkipMethods_ {""}; bool traceInline_ {false}; + bool traceValueNumbering_{false}; size_t maxInlineBytecodes_ {45}; std::string targetCompilerMode_ {""}; std::string hapPath_ {""}; diff --git a/test/aottest/global_value_numbering_ts/BUILD.gn b/test/aottest/global_value_numbering_ts/BUILD.gn new file mode 100644 index 0000000000..a272690ae0 --- /dev/null +++ b/test/aottest/global_value_numbering_ts/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("global_value_numbering_ts") { + deps = [] +} diff --git a/test/aottest/global_value_numbering_ts/expect_output.txt b/test/aottest/global_value_numbering_ts/expect_output.txt new file mode 100644 index 0000000000..9108220aca --- /dev/null +++ b/test/aottest/global_value_numbering_ts/expect_output.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Hello +Hello +Hello +Hello +1 +4 +-2 +6 diff --git a/test/aottest/global_value_numbering_ts/global_value_numbering_ts.ts b/test/aottest/global_value_numbering_ts/global_value_numbering_ts.ts new file mode 100644 index 0000000000..c326920d51 --- /dev/null +++ b/test/aottest/global_value_numbering_ts/global_value_numbering_ts.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare function print(n:string):string; +declare function print(n:number):string; + +// CMP +//[compiler] Found a replaceable node, before -> after +//[compiler] {"id":212, "op":"ICMP", "MType":"I1, bitfield=0xa, type=NJS_VALUE-GT(M=0, L=0), stamp=10, mark=2, ","in":[[], [], [55, 205], [], []], "out":[213]}, +//[compiler] {"id":209, "op":"ICMP", "MType":"I1, bitfield=0xa, type=NJS_VALUE-GT(M=0, L=0), stamp=10, mark=3, ","in":[[], [], [55, 205], [], []], "out":[210]}, + +//[compiler] {"id":55, "op":"VALUE_SELECTOR", "MType":"I32, bitfield=0x0, type=NJS_VALUE-GT(M=0, L=0), stamp=9, mark=3, ","in":[[16], [], [227, 217], [], []], "out":[215, 212, 209, 206, 102, 88, 94, 97, 99, 85, 83, 80, 71]}, +//[compiler] {"id":205, "op":"CONSTANT", "MType":"I32, bitfield=0x1, type=NJS_VALUE-GT(M=0, L=0), stamp=9, mark=3, ","in":[[], [], [], [], []], "out":[227, 224, 221, 215, 212, 209]}, + +for (var i:number = 0; i < 10; i++) { + if (i <= 1) { + print("Hello"); + } + if (i <= 1) { + print("Hello"); + } +} + +// TypedOperator + +//[compiler] Found a replaceable node, before -> after +//[compiler] {"id":669, "op":"FDIV", "MType":"F64, bitfield=0x0, type=NJS_VALUE-GT(M=0, L=0), stamp=10, mark=2, ","in":[[], [], [608, 610], [], []], "out":[670]}, +//[compiler] {"id":664, "op":"FDIV", "MType":"F64, bitfield=0x0, type=NJS_VALUE-GT(M=0, L=0), stamp=10, mark=3, ","in":[[], [], [608, 610], [], []], "out":[665]}, +function div(a: number, b: number): number { + let sum = 0; + if (a > 0) { + let k = a / b; + sum += k; + } + if (b > 0) { + let k = a / b; + sum += k; + } + return sum; +} +print(div(1,2)); + +function mul(a: number, b: number): number { + let sum = 0; + if (a > 0) { + let k = a * b; + sum += k; + } + if (b > 0) { + let k = a * b; + sum += k; + } + return sum; +} +print(mul(1,2)); + +function sub(a: number, b: number): number { + let sum = 0; + if (a > 0) { + let k = a - b; + sum += k; + } + if (b > 0) { + let k = a - b; + sum += k; + } + return sum; +} +print(sub(1,2)); + + +function add(a: number, b: number): number { + let sum = 0; + if (a > 0) { + let k = a + b; + sum += k; + } + if (b > 0) { + let k = a + b; + sum += k; + } + return sum; +} +print(add(1,2)); +