• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (c) 2024 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<%
17require 'yaml'
18
19Instruction.class_eval do
20  def get_input_idx(index, kind)
21    res = 0
22    index += 1 if has_dst?
23    acc_and_operands.each_with_index do |op, i|
24      break if i == index
25      res += 1 if op.send(kind)
26    end
27    res
28  end
29
30  def inputs
31    @inputs ||= acc_and_operands.select { |x| !x.dst? }
32  end
33
34  def has_dst?
35    !acc_and_operands.empty? && acc_and_operands.first.dst?
36  end
37
38  def get_input_string(index)
39    op = inputs[index]
40    return "GetDefinitionAcc()" if op.acc?
41    return "FindOrCreateConstant(instruction->GetImm<#{get_format}, #{get_input_idx(index, :imm?)}>())" if op.imm?
42    return "GetDefinition(instruction->GetId(#{get_input_idx(index, :id?)}).GetOffset())" if op.id?
43    raise "Invalid operand" unless op.reg?
44    return "GetDefinition(instruction->GetVReg(#{get_input_idx(index, :reg?)}))"
45  end
46
47  def get_format
48    return "BytecodeInst::Format::#{format.pretty.upcase}"
49  end
50
51end
52
53def get_type(type)
54  @type_map ||= {
55    'u1' => 'DataType::BOOL',
56    'i8' => 'DataType::INT8',
57    'i16' => 'DataType::INT16',
58    'i32' => 'DataType::INT32',
59    'i64' => 'DataType::INT64',
60    'u8' => 'DataType::UINT8',
61    'u16' => 'DataType::UINT16',
62    'u32' => 'DataType::UINT32',
63    'u64' => 'DataType::UINT64',
64    'b32' => 'DataType::UINT32',
65    'b64' => 'DataType::UINT64',
66    'f32' => 'DataType::FLOAT32',
67    'f64' => 'DataType::FLOAT64',
68    'ref' => 'DataType::REFERENCE',
69    'any' => 'DataType::ANY',
70    'i8[]' => 'DataType::INT8',
71    'i16[]' => 'DataType::INT16',
72    'i32[]' => 'DataType::INT32',
73    'i64[]' => 'DataType::INT64',
74    'u8[]' => 'DataType::UINT8',
75    'u16[]' => 'DataType::UINT16',
76    'u32[]' => 'DataType::UINT32',
77    'u64[]' => 'DataType::UINT64',
78    'b32[]' => 'DataType::UINT32',
79    'b64[]' => 'DataType::UINT64',
80    'f32[]' => 'DataType::FLOAT32',
81    'f64[]' => 'DataType::FLOAT64',
82    'ref[]' => 'DataType::REFERENCE',
83    'none' => 'DataType::NO_TYPE'}
84  return 'DataType::ANY' if @type_map[type].nil?
85  #  raise "Unknown type #{type}" if @type_map[type].nil?
86  return @type_map[type]
87end
88
89def get_template_by_inst(inst)
90  if (inst.namespace == "ecmascript")
91    return "ecma"
92  else
93    return get_template(inst.stripped_mnemonic)
94  end
95end
96
97def get_template(mn)
98  @tmpl_map ||= {
99    /ldarr/ => "ldarr",
100    /starr$/ => "starr",
101    /^return.*/ => "return",
102    /^[uf]?cmp/ => "cmp",
103    /^[ifu][813264]{1,2}to[ifu][813264]{1,2}$/ => "cast",
104    /^j(eq|ne|lt|gt|le|ge|stricteq|nstricteq)(z|null|undefined)?$/ => "if",
105    /^jmp.*/ => "jump",
106    /(fdiv|fmod|add|sub|mul|and|or|xor|ashr|shr|shl|neg|not)[2i]?$/ => "binop",
107    /^(div|mod)u?[2i]?$/ => "binop_z",
108    /^inci$/ => "inci",
109    /^movi?$/ => "mov",
110    /^fmovi?$/ => "fmovi",
111    /^sta$/ => "sta",
112    /^ldai?$/ => "lda",
113    /^fldai?$/ => "fldai",
114    /^lenarr$/ => "lenarr",
115    /^newarr$/ => "newarr",
116    /^call/ => "call",
117    /^newobj/ => "newobj",
118    /^initobj/ => "initobj",
119    /^ldobj/ => "ldobj",
120    /^stobj/ => "stobj",
121    /^ldstatic/ => "ldstatic",
122    /^ststatic/ => "ststatic",
123    /^isinstance/ => "isinstance",
124    /^checkcast/ => "checkcast",
125    /^throw/ => "throw",
126    /^monitor/ => "monitor",
127    /^nop/ => "nop",
128    /^builtin/ => "builtin",
129    /^ecma/ => "ecma",
130    /^$/ => "unimplemented"
131  }
132  res = @tmpl_map.select { |k, v| mn.match k }
133  raise "Template not found or multiple match: #{mn}" unless res.size == 1
134  return res.first[1]
135end
136
137def template(name, inst, indent, context = {})
138  @inst_yaml ||= YAML.load_file(File.join(File.dirname(__FILE__), 'inst_templates.yaml'))
139  raise "Template '#{name}' not found in templates file" unless @inst_yaml['templates'].key? name
140  indent + ERB.new(@inst_yaml['templates'][name], nil, '%-').result(binding).gsub("\n", "\n#{indent}")
141end
142
143def get_cc(inst)
144  return 'ark::compiler::ConditionCode::CC_EQ' if inst.opcode.start_with? 'jeq'
145  return 'ark::compiler::ConditionCode::CC_NE' if inst.opcode.start_with? 'jne'
146  return 'ark::compiler::ConditionCode::CC_LT' if inst.opcode.start_with? 'jlt'
147  return 'ark::compiler::ConditionCode::CC_GT' if inst.opcode.start_with? 'jgt'
148  return 'ark::compiler::ConditionCode::CC_LE' if inst.opcode.start_with? 'jle'
149  return 'ark::compiler::ConditionCode::CC_GE' if inst.opcode.start_with? 'jge'
150  return 'ark::compiler::ConditionCode::CC_EQ' if inst.opcode.start_with? 'jstricteq'
151  return 'ark::compiler::ConditionCode::CC_NE' if inst.opcode.start_with? 'jnstricteq'
152  raise 'get_cc: wrong opcode #{inst.opcode}'
153end
154
155%>
156
157#include "libabckit/src/irbuilder_dynamic/inst_builder_dyn.h"
158#include "libabckit/src/irbuilder_dynamic/ir_builder_dyn.h"
159#include "static_core/compiler/optimizer/ir/inst.h"
160#include "libabckit/src/irbuilder_dynamic/bytecode_inst.h"
161#include "libabckit/src/irbuilder_dynamic/bytecode_inst-inl.h"
162#include "libabckit/src/irbuilder_dynamic/inst_builder_dyn-inl.h"
163
164namespace libabckit {
165
166% additional_deprecated_opcodes = ["JNSTRICTEQUNDEFINED",
167%                                 "JSTRICTEQUNDEFINED",
168%                                 "JNEUNDEFINED",
169%                                 "JEQUNDEFINED",
170%                                 "JNSTRICTEQNULL",
171%                                 "JSTRICTEQNULL",
172%                                 "JNENULL",
173%                                 "JEQNULL",
174%                                 "JNSTRICTEQZ",
175%                                 "JSTRICTEQZ",
176%                                 "STTOGLOBALRECORD",
177%                                 "STCONSTTOGLOBALRECORD",
178%                                 "CREATEREGEXPWITHLITERAL",
179%                                 "CLOSEITERATOR"]
180
181% replaced_opcodes = ["JNSTRICTEQ",
182%                    "JSTRICTEQ",
183%                    "JNE",
184%                    "JEQ"]
185
186% skipped_opcodes = additional_deprecated_opcodes + replaced_opcodes
187
188// NOLINTNEXTLINE(readability-function-size)
189void InstBuilder::BuildInstruction(const BytecodeInst* instruction) {
190    switch(instruction->GetOpcode()) {
191% Panda::instructions.each do |inst|
192%   if !(inst.opcode.upcase.include? "DEPRECATED_") && !(skipped_opcodes.include? inst.mnemonic.upcase)
193%     tmpl = inst.mnemonic.include?('polymorphic') ? 'unimplemented' : get_template_by_inst(inst)
194    // NOLINTNEXTLINE(bugprone-branch-clone)
195    case BytecodeInst::Opcode::<%= inst.opcode.upcase %>: {
196%     if tmpl == 'unimplemented'
197       // Not implemented
198       failed_ = true;
199%     else
200<%= template(tmpl, inst, ' ' * 8) %>
201%     end
202      return; }
203%   end
204% end
205    }
206}
207
208// NOLINTNEXTLINE(readability-function-size)
209int64_t InstBuilder::GetInstructionJumpOffset(const BytecodeInst* inst) {
210    switch(inst->GetOpcode()) {
211% Panda::instructions.each do |inst|
212%   if !(inst.opcode.upcase.include? "DEPRECATED_") && !(skipped_opcodes.include? inst.mnemonic.upcase)
213    // NOLINTNEXTLINE(bugprone-branch-clone)
214    case BytecodeInst::Opcode::<%= inst.opcode.upcase %>:
215%     if inst.jump?
216        return inst->GetImm<BytecodeInst::Format::<%= inst.format.pretty.upcase %>, 0, true>();
217%     else
218        return INVALID_OFFSET;
219%     end
220%   end
221% end
222    }
223    return INVALID_OFFSET;
224}
225
226// NOTE(vpukhov): Move this logic from core into plugins/ecmascript
227// Currently we support two strategies for building IR from ecma.* instructions:
228//   1) Each ecma.* instruction is translated to a corresponding intrinsic call.
229//      This is used for bytecode optimizer and slow paths during compiling
230//      to native code (in both JIT and AOT modes).
231//   2) Semantics of each ecma.* instruction is taken from the corresponding
232//      IRtoC handler and is inlined into compiled function.
233//      This is used only for native compilation (again, both JIT and AOT).
234// InstBuilder::BuildEcma selects proper strategy and calls relevant builder.
235
236// NOLINTNEXTLINE(readability-function-size)
237void InstBuilder::BuildEcma([[maybe_unused]] const BytecodeInst* bcInst)
238{
239#ifdef ENABLE_BYTECODE_OPT
240    switch (bcInst->GetOpcode()) {
241% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst|
242%   if !(inst.opcode.upcase.include? "DEPRECATED_") && !(skipped_opcodes.include? inst.mnemonic.upcase)
243        case BytecodeInst::Opcode::<%= inst.opcode.upcase %>:
244%     if inst.compilable? && inst.inlinable?
245            // +compilable, +inlinable: ecma.* -> intrinsics for BCO, inline IRtoC otherwise:
246            if (GetGraph()->IsBytecodeOptimizer()) {
247                BuildEcmaAsIntrinsics(bcInst);
248            } else {
249                BuildEcmaAsIntrinsics<true>(bcInst);
250            }
251%     elsif inst.compilable?
252            // +compilable, -inlinable: ecma.* -> intrinsics for all scenarios:
253            BuildEcmaAsIntrinsics(bcInst);
254%     else
255%        abort "isa.yaml inconsistency: #{inst.opcode.upcase} is not compilable, but inlinable" if inst.inlinable?
256            // -compilable, -inlinable: ecma.* -> intrinsics for BCO, fail IR builder otherwise:
257            if (GetGraph()->IsBytecodeOptimizer()) {
258                BuildEcmaAsIntrinsics(bcInst);
259            } else {
260                failed_ = true;
261            }
262%     end
263            return;
264%   end
265% end
266        default: {
267            failed_ = true;
268            std::cerr << "Unknown ecma.* opcode: " << static_cast<int>(bcInst->GetOpcode()) << std::endl;
269            return;
270        }
271    }
272#endif
273}
274
275template <bool WITH_SPECULATIVE>
276void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInst* bcInst) // NOLINT(readability-function-size)
277{
278    switch (bcInst->GetOpcode()) {
279% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst|
280%   if !(inst.opcode.upcase.include? "DEPRECATED_") && !(skipped_opcodes.include? inst.mnemonic.upcase)
281%     opc = inst.opcode.upcase
282%     name = opc
283%     acc_read = inst.acc.include?("in")
284%     acc_write = inst.acc.include?("out")
285%     ret_type = acc_write ? "ark::compiler::DataType::ANY" : "ark::compiler::DataType::VOID"
286%     iname = inst.intrinsic_name ? inst.intrinsic_name : opc
287%     intrinsicId = "ark::compiler::RuntimeInterface::IntrinsicId::DYN_" + iname
288%     intrinsic_external_js_id = "ark::compiler::RuntimeInterface::IntrinsicId::DYN_" + opc
289        case BytecodeInst::Opcode::<%= opc %>:
290        {
291            auto intrinsicId = <%= intrinsic_external_js_id %>;
292
293            auto inst = GetGraph()->CreateInstIntrinsic(<%= ret_type %>, GetPc(bcInst->GetAddress()), intrinsicId);
294
295%     params_arr = inst.operands
296%     format = "BytecodeInst::Format::" + inst.format.pretty.upcase
297%     range_should_exclude_last = (name.include? "NEWOBJRANGE") || (name.include? "SUPERCALLTHISRANGE") || (name.include? "SUPERCALLARROWRANGE") || (name.include? "CALLRANGE")
298%     is_range_call = (name.include? "CALLTHISRANGE") || (name.include? "CREATEOBJECTWITHEXCLUDEDKEYS") || range_should_exclude_last
299%     need_newtarget = (name.include? "SUPERCALLSPREAD") || (name.include? "SUPERCALL")
300%     num_vregs = params_arr.select{|b| b.reg?}.length
301%     num_imms = params_arr.select{|b| b.imm?}.length
302%     num_ids = params_arr.select{|b| b.id?}.length
303%     num_inputs = acc_read ? num_vregs + 1 : num_vregs + 0
304%     if range_should_exclude_last
305%       num_inputs = num_inputs - 1
306%     end
307%     if need_newtarget
308%       num_inputs = num_inputs + 1
309%     end
310%     has_ic_slot = inst.properties.include?('ic_slot') || inst.properties.include?('jit_ic_slot')
311%     if is_range_call
312            size_t argsCount = <%= num_inputs %>U + static_cast<size_t>(bcInst->GetImm<<%= format %>, <%= has_ic_slot ? 1 : 0 %>, false>());
313%     else
314            size_t argsCount {<%= num_inputs %>U};
315%     end
316%     if need_newtarget
317            if (GetGraph()->IsBytecodeOptimizer()) {
318                --argsCount;
319            }
320%     end
321            inst->ReserveInputs(argsCount);
322            inst->AllocateInputTypes(GetGraph()->GetAllocator(), argsCount);
323            // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
324            if constexpr (WITH_SPECULATIVE) {
325                inst->SetInlined(true);
326            }
327%     if acc_read
328            {
329                auto input = GetDefinitionAcc();
330                inst->AppendInput(input);
331                inst->AddInputType(ark::compiler::DataType::ANY);
332            }
333%     end
334%     imm_index = 0
335%     vreg_index = 0
336%     id16_index = 0
337%     params_arr.each do |param|
338%       if param.imm?
339            auto imm<%= imm_index %> = static_cast<uint32_t>(bcInst->GetImm<<%= format %>, <%= imm_index %>, false>());
340            inst->AddImm(GetGraph()->GetAllocator(), imm<%= imm_index %>);
341%           imm_index = imm_index + 1
342%       elsif param.reg?
343            {
344                auto input = GetDefinition(bcInst->GetVReg(<%= vreg_index %>));
345                inst->AppendInput(input);
346                inst->AddInputType(ark::compiler::DataType::ANY);
347            }
348%           vreg_index = vreg_index + 1
349%       elsif param.id?
350%         if inst.properties.include?("method_id")
351            auto mIdx<%= id16_index %> = bcInst->GetId(<%= id16_index %>).AsRawValue();
352            mIdx<%= id16_index %> = GetRuntime()->ResolveMethodIndex(GetGraph()->GetMethod(), mIdx<%= id16_index %>);
353            inst->AddImm(GetGraph()->GetAllocator(), mIdx<%= id16_index %>);
354%         elsif inst.properties.include?("string_id")
355            auto stringId<%= id16_index %> = bcInst->GetId(<%= id16_index %>).AsRawValue();
356            stringId<%= id16_index %> = GetRuntime()->ResolveMethodIndex(GetGraph()->GetMethod(), stringId<%= id16_index %>);
357            inst->AddImm(GetGraph()->GetAllocator(), stringId<%= id16_index %>);
358%         elsif inst.properties.include?("literalarray_id")
359            auto literalarrayId<%= id16_index %> = bcInst->GetId(<%= id16_index %>).AsRawValue();
360            literalarrayId<%= id16_index %> = GetRuntime()->ResolveMethodIndex(GetGraph()->GetMethod(), literalarrayId<%= id16_index %>);
361            inst->AddImm(GetGraph()->GetAllocator(), literalarrayId<%= id16_index %>);
362%         end
363%         id16_index = id16_index + 1
364%       end
365%     end
366%     if is_range_call
367%           range_reg_idx = (name.include? "CREATEOBJECTWITHEXCLUDEDKEYS") ? 1 : 0
368%           imm_arg_num = has_ic_slot ? 'imm1' : 'imm0'
369%           num_actual_vregs = range_should_exclude_last ? imm_arg_num : imm_arg_num + " + 1"
370            size_t startReg = bcInst->GetVReg(<%= range_reg_idx %>);
371            for (uint32_t i = 1; i < <%= num_actual_vregs %>; ++i) {
372                auto input = GetDefinition(startReg + i);
373                inst->AppendInput(input);
374                inst->AddInputType(ark::compiler::DataType::ANY);
375            }
376%     end
377            AddInstruction(inst);
378%     if acc_write
379            UpdateDefinitionAcc(inst);
380%     end
381            break; }
382%   end
383% end
384        default:
385            failed_ = true;
386            std::cerr << "unknown Ecma opcode!" << static_cast<int>(bcInst->GetOpcode()) << std::endl;
387  }
388}
389}  // namespace libabckit
390