1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/compiler/ts_hcr_opt_pass.h"
17#include "ecmascript/jit/jit.h"
18
19namespace panda::ecmascript::kungfu {
20
21GateRef TSHCROptPass::VisitGate(GateRef gate)
22{
23    AddProfiling(gate);
24    auto opcode = acc_.GetOpCode(gate);
25    switch (opcode) {
26        case OpCode::TYPED_BINARY_OP:
27            return VisitTypedBinaryOp(gate);
28        default:
29            break;
30    }
31    return Circuit::NullGate();
32}
33
34void TSHCROptPass::AddProfiling(GateRef gate)
35{
36    if (IsTypedOpProfiling() && acc_.UseForTypeOpProfilerGate(gate)) {
37        Environment env(gate, circuit_, &builder_);
38        OpCode opcode  = acc_.GetOpCode(gate);
39        auto opcodeGate = builder_.Int32(static_cast<uint32_t>(opcode));
40        GateRef constOpcode = builder_.Int32ToTaggedInt(opcodeGate);
41        GateRef traceGate = builder_.CallRuntime(acc_.GetGlueFromArgList(), RTSTUB_ID(ProfileTypedOp),
42                                                 acc_.GetDep(gate), { constOpcode }, gate);
43        acc_.SetDep(gate, traceGate);
44        builder_.SetDepend(acc_.GetDep(gate));
45    }
46}
47
48GateRef TSHCROptPass::VisitTypedBinaryOp(GateRef gate)
49{
50    if (acc_.HasStringType(gate)) {
51        return VisitStringBinOp(gate);
52    }
53    return Circuit::NullGate();
54}
55
56GateRef TSHCROptPass::VisitStringBinOp(GateRef gate)
57{
58    TypedBinOp op = acc_.GetTypedBinaryOp(gate);
59    switch (op) {
60        case TypedBinOp::TYPED_EQ: {
61            Jit::JitLockHolder lock(compilationEnv_, "VisitStringEqual");
62            return VisitStringEqual(gate);
63        }
64        default:
65            return Circuit::NullGate();
66    }
67}
68
69GateRef TSHCROptPass::VisitStringEqual(GateRef gate)
70{
71    Environment env(gate, circuit_, &builder_);
72    GateRef left = acc_.GetValueIn(gate, 0);
73    GateRef right = acc_.GetValueIn(gate, 1);
74    if (acc_.IsConstString(left) && acc_.IsConstString(right)) {
75        return ConvertStringEqualToConst(left, right);
76    }
77
78    if (IsSingleCharString(left) && IsSingleCharString(right)) {
79        return ConvertToSingleCharComparison(left, right);
80    }
81
82    if (IsNotLoadStrOrStringLoadElement(left) || IsNotLoadStrOrStringLoadElement(right)) {
83        return Circuit::NullGate();
84    }
85
86    if (IsSingleCharString(left) || IsSingleCharString(right)) {
87        return builder_.Boolean(false);
88    }
89
90    return Circuit::NullGate();
91}
92
93GateRef TSHCROptPass::ConvertStringEqualToConst(GateRef left, GateRef right)
94{
95    uint32_t leftId = acc_.GetStringIdFromLdaStrGate(left);
96    uint32_t rightId = acc_.GetStringIdFromLdaStrGate(right);
97
98    auto leftMethodOffset = acc_.TryGetMethodOffset(left);
99    auto rightMethodOffset = acc_.TryGetMethodOffset(right);
100    JSTaggedValue leftStr = GetStringFromConstantPool(leftMethodOffset, leftId);
101    // jit: disallow alloc jsstring, across gc point
102    JSTaggedValue rightStr = GetStringFromConstantPool(rightMethodOffset, rightId, false);
103    if (leftStr == JSTaggedValue::Undefined() || rightStr == JSTaggedValue::Undefined()) {
104        return Circuit::NullGate();
105    }
106    if (leftStr == rightStr) {
107        return builder_.Boolean(true);
108    }
109    return builder_.Boolean(false);
110}
111
112bool TSHCROptPass::IsSingleCharString(GateRef gate)
113{
114    if (acc_.IsConstString(gate)) {
115        uint32_t strId = acc_.GetStringIdFromLdaStrGate(gate);
116        auto methodOffset = acc_.TryGetMethodOffset(gate);
117        JSTaggedValue str = GetStringFromConstantPool(methodOffset, strId);
118        if (str.IsUndefined()) {
119            return false;
120        }
121        return EcmaStringAccessor(str).GetLength() == 1;
122    }
123    return acc_.IsSingleCharGate(gate);
124}
125
126bool TSHCROptPass::IsNotLoadStrOrStringLoadElement(GateRef gate)
127{
128    OpCode op = acc_.GetOpCode(gate);
129    if (op == OpCode::JS_BYTECODE) {
130        EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
131        return ecmaOpcode != EcmaOpcode::LDA_STR_ID16;
132    }
133    if (op == OpCode::LOAD_ELEMENT) {
134        return acc_.GetTypedLoadOp(gate) != TypedLoadOp::STRING_LOAD_ELEMENT;
135    }
136    return true;
137}
138
139GateRef TSHCROptPass::ConvertConstSingleCharToInt32(GateRef gate)
140{
141    ASSERT(acc_.IsConstString(gate));
142    uint32_t strId = acc_.GetStringIdFromLdaStrGate(gate);
143    auto methodOffset = acc_.TryGetMethodOffset(gate);
144    JSTaggedValue str = GetStringFromConstantPool(methodOffset, strId);
145    if (str == JSTaggedValue::Undefined()) {
146        return Circuit::NullGate();
147    }
148    ASSERT(EcmaStringAccessor(str).GetLength() == 1);
149    uint16_t strToInt = EcmaStringAccessor(str).Get(0);
150    return builder_.Int32(strToInt);
151}
152
153GateRef TSHCROptPass::ConvertToSingleCharComparison(GateRef left, GateRef right)
154{
155    ASSERT(!acc_.IsConstString(left) || !acc_.IsConstString(right));
156    if (acc_.IsConstString(left)) {
157        left = ConvertConstSingleCharToInt32(left);
158    } else if (acc_.IsConstString(right)) {
159        right = ConvertConstSingleCharToInt32(right);
160    }
161    // change string binary operator to int binary operator
162    return builder_.TypedBinaryOp<TypedBinOp::TYPED_EQ>(left, right, ParamType::IntType());
163}
164}  // namespace panda::ecmascript::kungfu
165