# Copyright (c) 2021-2022 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.

templates:
  binop: |-
    % regex_arithm = /(fdiv|fmod|add|sub|mul|and|or|xor|ashr|shr|shl|neg|not)[2i]?/
    % opc = inst.stripped_mnemonic.match regex_arithm
    % raise "Wrong binop instruction" unless opc
    % opc = opc[1].capitalize.gsub('Ashr', 'AShr').gsub('Fdiv', 'Div').gsub('Fmod', 'Mod')
    auto inst = graph_->CreateInst<%= opc %>(<%= get_type(inst.dtype) %>, GetPc(instruction->GetAddress()));
    <%=template('operands', inst, '')-%>
    AddInstruction(inst);
  binop_z: |-
    % opc = inst.stripped_mnemonic =~ /div/ ? 'Div' : 'Mod'
    auto inst_save_state = CreateSaveState(Opcode::SaveState, GetPc(instruction->GetAddress()));
    auto inst_check = graph_->CreateInstZeroCheck(<%= get_type(inst.dtype) %>, GetPc(instruction->GetAddress()));
    % if inst.acc_and_operands.last.imm?
    if (graph_->IsBytecodeOptimizer()) {
        inst_check->SetInput(0, FindOrCreate32BitConstant(instruction->GetImm<<%= inst.get_format %>, 0>()));
    } else {
        inst_check->SetInput(0, <%= inst.get_input_string(1) %>);
    }
    % else
    inst_check->SetInput(0, <%= inst.get_input_string(1) %>);
    % end
    inst_check->SetInput(1, inst_save_state);
    auto inst = graph_->CreateInst<%= opc %>(<%= get_type(inst.dtype) %>, GetPc(instruction->GetAddress()));
    inst->SetInput(0, <%= inst.get_input_string(0) %>);
    inst->SetInput(1, inst_check);
    UpdateDefinitionAcc(inst);
    AddInstruction(inst_save_state);
    AddInstruction(inst_check);
    AddInstruction(inst);
  inci: |-
    auto inst = graph_->CreateInstAdd(<%= get_type(inst.dtype) %>, GetPc(instruction->GetAddress()));
    inst->SetInput(0, GetDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>()));
    if (graph_->IsBytecodeOptimizer()) {
        inst->SetInput(1, FindOrCreate32BitConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    } else {
        inst->SetInput(1, FindOrCreateConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    }
    AddInstruction(inst);
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), inst);
  cast: |-
    auto inst = graph_->CreateInstCast(<%= get_type(inst.dtype) %>, GetPc(instruction->GetAddress()));
    auto input = GetDefinitionAcc();
    inst->SetInput(0, input);
    inst->SetOperandsType(<%= get_type(inst.type(0)) %>);
    if (!input->HasType()) {
        input->SetType(<%= get_type(inst.type(0)) %>);
    }
    UpdateDefinitionAcc(inst);
    AddInstruction(inst);
  cmp: |-
    % if inst.mnemonic.include? "obj"
    auto inst = graph_->CreateInstCompare(DataType::BOOL, GetPc(instruction->GetAddress()), ConditionCode::CC_NE);
    % else
    auto inst = graph_->CreateInstCmp(<%= get_type(inst.dtype) %>, GetPc(instruction->GetAddress()));
    % end
    inst->SetInput(0, GetDefinitionAcc());
    inst->SetInput(1, GetDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>()));
    inst->SetOperandsType(<%= get_type(inst.type(0)) %>);
    % if inst.stripped_mnemonic.start_with? 'fcmp'
    inst->Set<%= inst.opcode =~ /^fcmpg/ ? 'Fcmpg' : 'Fcmpl' %>();
    % end
    AddInstruction(inst);
    UpdateDefinitionAcc(inst);
  if: |-
    auto inst = graph_->CreateInstCompare(DataType::BOOL, GetPc(instruction->GetAddress()), <%= get_cc(inst) %>);
    inst->SetOperandsType(<%= get_type(inst.type(0)) %>);
    % if inst.acc_and_operands.size > 2
    inst->SetInput(1, GetDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>()));
    % else
    inst->SetInput(1, FindOrCreateConstant(0));
    % end
    inst->SetInput(0, GetDefinitionAcc());
    auto inst_jump = graph_->CreateInstIfImm(DataType::NO_TYPE, GetPc(instruction->GetAddress()), ConditionCode::CC_NE, 0);
    inst_jump->SetOperandsType(DataType::BOOL);
    inst_jump->SetInput(0, inst);
    inst_jump->SetMethod(graph_->GetMethod());
    AddInstruction(inst);
    AddInstruction(inst_jump);
  jump: |-
  mov: |-
    % if inst.acc_and_operands[1].imm?
    %   if inst.mnemonic == "movi"
    if (graph_->IsBytecodeOptimizer()) {
        UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), FindOrCreate32BitConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    } else {
        UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), FindOrCreateConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    }
    %   else
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), FindOrCreateConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    %   end
    % else
    % raise "Unsupported instruction type" unless inst.acc_and_operands[1].reg?
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), GetDefinition(instruction->GetVReg<<%=inst.get_format%>, 1>()));
    % end
  fmovi: |-
    % if inst.mnemonic == "fmovi"
    auto imm = bit_cast<float>(instruction->GetImm<<%=inst.get_format%>, 0>());
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), FindOrCreateFloatConstant(imm));
    % else
    auto imm = bit_cast<double>(instruction->GetImm<<%=inst.get_format%>, 0>());
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), FindOrCreateDoubleConstant(imm));
    % end
  sta: |-
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), GetDefinitionAcc());
  lda: |-
    % if inst.acc_and_operands[1].imm?
    %   if inst.mnemonic == "ldai"
    if (graph_->IsBytecodeOptimizer()) {
        UpdateDefinitionAcc(FindOrCreate32BitConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    } else {
        UpdateDefinitionAcc(FindOrCreateConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    }
    %   else
    UpdateDefinitionAcc(FindOrCreateConstant(instruction->GetImm<<%=inst.get_format%>, 0>()));
    %     if inst.properties.include? "dynamic"
    BuildCastToAnyNumber(instruction);
    %     end
    %   end
    % elsif inst.acc_and_operands[1].id?
    BuildLoadFromPool<Opcode::<%= inst.opcode =~ /lda_type/ ? 'LoadType' : 'LoadString' %>>(instruction);
    % else
    % raise "Unsupported instruction type" unless inst.acc_and_operands[1].reg?
    UpdateDefinitionAcc(GetDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>()));
    % end
  fldai: |-
    auto imm = bit_cast<double>(instruction->GetImm<<%=inst.get_format%>, 0>());
    UpdateDefinitionAcc(FindOrCreateDoubleConstant(imm));
    % if inst.properties.include? "dynamic"
    BuildCastToAnyNumber(instruction);
    % end
  operands: |-
    % inst.inputs.each_with_index do |op, idx|
    %   if op.imm?
    if (graph_->IsBytecodeOptimizer()) {
        inst->SetInput(<%= idx %>, FindOrCreate32BitConstant(instruction->GetImm<<%= inst.get_format %>, 0>()));
    } else {
        inst->SetInput(<%= idx %>, <%= inst.get_input_string(idx) %>);
    }
    %   else
    inst->SetInput(<%= idx %>, <%= inst.get_input_string(idx) %>);
    %   end
    % end
    % if inst.has_dst?
    %   if inst.acc_and_operands.first.acc?
    UpdateDefinitionAcc(inst);
    %   else
    UpdateDefinition(instruction->GetVReg<<%=inst.get_format%>, 0>(), inst);
    %   end
    % end
  return: |-
    % opcode = inst.opcode.include?('void') ? 'ReturnVoid' : 'Return'
    auto inst = graph_->CreateInst<%= opcode %>(GetCurrentMethodReturnType(), GetPc(instruction->GetAddress()));
    <%=template('operands', inst, '')-%>
    AddInstruction(inst);
  ecma: |-
    % name = inst.opcode.upcase.split('')[1]
    % case name
    % when "RETURNUNDEFINED"
    auto cvat_input = FindOrCreateConstant(0);
    cvat_input->SetType(DataType::Type::INT64);
    auto cvat = graph->CreateInstCastValueToAnyType(0);
    cvat->SetAnyType(panda::compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE);
    cvat->SetInput(0, cvat_input);
    auto inst = graph_->CreateInstReturn(DataType::ANY, GetPc(instruction->GetAddress()));
    inst->SetInput(0, cvat);
    AddInstruction(cvat);
    AddInstruction(inst);
    % when "RETURN"
    auto inst = graph_->CreateInstReturn(DataType::ANY, GetPc(instruction->GetAddress()));
    inst->SetInput(0, GetDefinitionAcc());
    AddInstruction(inst);
    % when "JFALSE", "JTRUE"
    % cmp_imm = name == "JFALSE" ? 0 : 1
    auto cvat_input = FindOrCreateConstant(<%= cmp_imm %>);
    cvat_input->SetType(DataType::INT64);
    auto cvat = graph_->CreateInstCastValueToAnyType(0);
    cvat->SetAnyType(panda::compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE);
    cvat->SetInput(0, cvat_input);
    auto cmp_inst = graph_->CreateInstCompare(DataType::BOOL, GetPc(instruction->GetAddress()), ConditionCode::CC_EQ);
    cmp_inst->SetOperandsType(DataType::ANY);
    cmp_inst->SetInput(0, GetDefinitionAcc());
    cmp_inst->SetInput(1, cvat);
    auto jmp_inst = graph_->CreateInstIfImm(DataType::NO_TYPE, GetPc(instruction->GetAddress()), ConditionCode::CC_NE, 0);
    jmp_inst->SetOperandsType(DataType::BOOL);
    jmp_inst->SetInput(0, cmp_inst);
    AddInstruction(cvat);
    AddInstruction(cmp_inst);
    AddInstruction(jmp_inst);
    % else
    BuildEcma(instruction);
    % end
  nop: |-
  unimplemented: |-
    // TODO(msherstennikov): implement
