• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (c) 2021-2022 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_format}, #{get_input_idx(index, :id?)}>().GetOffset())" if op.id?
43    raise "Invalid operand" unless op.reg?
44    return "GetDefinition(instruction->GetVReg<#{get_format}, #{get_input_idx(index, :reg?)}>())"
45  end
46
47  def get_format
48    return "BytecodeInstruction::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__), '../ir_builder/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 'ConditionCode::CC_EQ' if inst.opcode.start_with? 'jeq'
145  return 'ConditionCode::CC_NE' if inst.opcode.start_with? 'jne'
146  return 'ConditionCode::CC_LT' if inst.opcode.start_with? 'jlt'
147  return 'ConditionCode::CC_GT' if inst.opcode.start_with? 'jgt'
148  return 'ConditionCode::CC_LE' if inst.opcode.start_with? 'jle'
149  return 'ConditionCode::CC_GE' if inst.opcode.start_with? 'jge'
150  return 'ConditionCode::CC_EQ' if inst.opcode.start_with? 'jstricteq'
151  return 'ConditionCode::CC_NE' if inst.opcode.start_with? 'jnstricteq'
152  raise 'get_cc: wrong opcode #{inst.opcode}'
153end
154
155%>
156
157#include "compiler_logger.h"
158#include "optimizer/ir_builder/inst_builder.h"
159#include "optimizer/ir_builder/ir_builder.h"
160#include "optimizer/ir/inst.h"
161#include "bytecode_instruction.h"
162#include "bytecode_instruction-inl.h"
163#include "optimizer/ir_builder/inst_builder-inl.h"
164#include "include/coretypes/tagged_value.h"
165#include "irtoc_builder.cpp"
166
167namespace panda::compiler {
168
169// NOLINTNEXTLINE(readability-function-size)
170void InstBuilder::BuildInstruction(const BytecodeInstruction* instruction) {
171    switch(instruction->GetOpcode()) {
172% Panda::instructions.each_with_index do |inst, idx|
173%   tmpl = inst.mnemonic.include?('polymorphic') ? 'unimplemented' : get_template_by_inst(inst)
174    // NOLINTNEXTLINE(bugprone-branch-clone)
175    case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: {
176%   if tmpl == 'unimplemented'
177       // Not implemented
178       failed_ = true;
179%   else
180<%= template(tmpl, inst, ' ' * 8) %>
181%   end
182        break;
183    }
184% end
185    }
186}
187
188// NOLINTNEXTLINE(readability-function-size)
189int64_t InstBuilder::GetInstructionJumpOffset(const BytecodeInstruction* inst) {
190    switch(inst->GetOpcode()) {
191% Panda::instructions.each_with_index do |inst, idx|
192    // NOLINTNEXTLINE(bugprone-branch-clone)
193    case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>:
194%   if inst.jump?
195        return inst->GetImm<BytecodeInstruction::Format::<%= inst.format.pretty.upcase %>, 0>();
196%   else
197        return INVALID_OFFSET;
198%   end
199% end
200    }
201    return INVALID_OFFSET;
202}
203
204// TODO(vpukhov): Move this logic from core into plugins/ecmascript
205// Currently we support two strategies for building IR from ecma.* instructions:
206//   1) Each ecma.* instruction is translated to a corresponding intrinsic call.
207//      This is used for bytecode optimizer and slow paths during compiling
208//      to native code (in both JIT and AOT modes).
209//   2) Semantics of each ecma.* instruction is taken from the corresponding
210//      IRtoC handler and is inlined into compiled function.
211//      This is used only for native compilation (again, both JIT and AOT).
212// InstBuilder::BuildEcma selects proper strategy and calls relevant builder.
213
214// NOLINTNEXTLINE(readability-function-size)
215void InstBuilder::BuildEcma([[maybe_unused]] const BytecodeInstruction* bc_inst)
216{
217#ifdef ENABLE_BYTECODE_OPT
218    switch (bc_inst->GetOpcode()) {
219% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst|
220        case BytecodeInstruction::Opcode::<%= inst.opcode.upcase %>: {
221%   if inst.compilable? && inst.inlinable?
222            // +compilable, +inlinable: ecma.* -> intrinsics for BCO, inline IRtoC otherwise:
223            if (GetGraph()->IsBytecodeOptimizer()) {
224                BuildEcmaAsIntrinsics(bc_inst);
225            } else {
226                BuildEcmaAsIntrinsics<true>(bc_inst);
227            }
228%   elsif inst.compilable?
229            // +compilable, -inlinable: ecma.* -> intrinsics for all scenarios:
230            BuildEcmaAsIntrinsics(bc_inst);
231%   else
232%      abort "isa.yaml inconsistency: #{inst.opcode.upcase} is not compilable, but inlinable" if inst.inlinable?
233            // -compilable, -inlinable: ecma.* -> intrinsics for BCO, fail IR builder otherwise:
234            if (GetGraph()->IsBytecodeOptimizer()) {
235                BuildEcmaAsIntrinsics(bc_inst);
236            } else {
237                failed_ = true;
238            }
239%   end
240            break;
241        }
242% end
243        default: {
244            failed_ = true;
245            LOG(ERROR, COMPILER) << "Unknown ecma.* opcode: " << static_cast<int>(bc_inst->GetOpcode());
246            return;
247        }
248    }
249#endif
250}
251
252template <bool with_speculative>
253void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // NOLINT(readability-function-size)
254{
255    switch (bc_inst->GetOpcode()) {
256% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst|
257%   opc = inst.opcode.upcase
258%   name = opc
259%     acc_read = inst.acc.include?("in")
260%     acc_write = inst.acc.include?("out")
261%     ret_type = acc_write ? "compiler::DataType::ANY" : "compiler::DataType::VOID"
262%     iname = inst.intrinsic_name ? inst.intrinsic_name : opc
263%     intrinsic_id = "compiler::RuntimeInterface::IntrinsicId::" + iname
264%     intrinsic_external_js_id = "compiler::RuntimeInterface::IntrinsicId::" + opc
265        case BytecodeInstruction::Opcode::<%= opc %>: {
266            #ifdef ARK_INTRINSIC_SET
267            auto intrinsic_id = <%= intrinsic_id %>;
268            #else
269            auto intrinsic_id = <%= intrinsic_external_js_id %>;
270            #endif
271            auto inst = GetGraph()->CreateInstIntrinsic(<%= ret_type %>, GetPc(bc_inst->GetAddress()), intrinsic_id);
272%     if inst.throwing?
273            inst->SetFlag(inst_flags::CAN_THROW);
274%     end
275%     if inst.exceptions.include?('x_throw') || inst.properties.include?('return')
276            inst->SetFlag(inst_flags::CF);
277            inst->SetFlag(inst_flags::TERMINATOR);
278%     end
279%     params_arr = inst.operands
280%     format = "BytecodeInstruction::Format::" + inst.format.pretty.upcase
281%     range_should_exclude_last = (name.include? "NEWOBJRANGE") || (name.include? "SUPERCALLTHISRANGE") || (name.include? "SUPERCALLARROWRANGE") || (name.include? "CALLRANGE")
282%     is_range_call = (name.include? "CALLTHISRANGE") || (name.include? "CREATEOBJECTWITHEXCLUDEDKEYS") || range_should_exclude_last
283%     need_newtarget = (name.include? "SUPERCALLSPREAD") || (name.include? "SUPERCALL")
284%     num_vregs = params_arr.select{|b| b.reg?}.length
285%     num_imms = params_arr.select{|b| b.imm?}.length
286%     num_ids = params_arr.select{|b| b.id?}.length
287%     num_inputs = acc_read ? num_vregs + 2 : num_vregs + 1
288%     if range_should_exclude_last
289%       num_inputs = num_inputs - 1
290%     end
291%     if need_newtarget
292%       num_inputs = num_inputs + 1
293%     end
294%     has_ic_slot = inst.properties.include?('ic_slot') || inst.properties.include?('jit_ic_slot')
295%     if is_range_call
296            size_t args_count = <%= num_inputs %>U + static_cast<size_t>(bc_inst->GetImm<<%= format %>, <%= has_ic_slot ? 1 : 0 %>>());
297%     else
298            size_t args_count {<%= num_inputs %>U};
299%     end
300%     if need_newtarget
301            if (GetGraph()->IsBytecodeOptimizer()) {
302                --args_count;
303            }
304%     end
305            inst->ReserveInputs(args_count);
306            inst->AllocateInputTypes(GetGraph()->GetAllocator(), args_count);
307            // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
308            if constexpr (with_speculative) {
309                inst->SetInlined(true);
310            }
311
312%     imm_index = 0
313%     vreg_index = 0
314%     id16_index = 0
315            auto inst_save_state = CreateSaveState(Opcode::SaveState, GetPc(bc_inst->GetAddress()));
316            AddInstruction(inst_save_state);
317%     params_arr.each do |param|
318%       if param.imm?
319            auto imm<%= imm_index %> = static_cast<uint32_t>(bc_inst->GetImm<<%= format %>, <%= imm_index %>>());
320            inst->AddImm(GetGraph()->GetAllocator(), imm<%= imm_index %>);
321%           imm_index = imm_index + 1
322%       elsif param.reg?
323            {
324                auto input = GetDefinition(bc_inst->GetVReg<<%= format %>, <%= vreg_index %>>());
325                // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
326                if constexpr (with_speculative) {
327                    input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state);
328                }
329                inst->AppendInput(input);
330                inst->AddInputType(DataType::ANY);
331            }
332%           vreg_index = vreg_index + 1
333%       elsif param.id?
334%         if inst.properties.include?("method_id")
335            auto m_idx<%= id16_index %> = bc_inst->template GetId<<%= format %>, <%= id16_index %>>().AsRawValue();
336            m_idx<%= id16_index %> = GetRuntime()->ResolveOffsetByIndex(GetGraph()->GetMethod(), m_idx<%= id16_index %>);
337            inst->AddImm(GetGraph()->GetAllocator(), m_idx<%= id16_index %>);
338%         elsif inst.properties.include?("string_id")
339            auto string_id<%= id16_index %> = bc_inst->template GetId<<%= format %>, <%= id16_index %>>().AsRawValue();
340            string_id<%= id16_index %> = GetRuntime()->ResolveOffsetByIndex(GetGraph()->GetMethod(), string_id<%= id16_index %>);
341            inst->AddImm(GetGraph()->GetAllocator(), string_id<%= id16_index %>);
342%         elsif inst.properties.include?("literalarray_id")
343            auto literalarray_id<%= id16_index %> = bc_inst->template GetId<<%= format %>, <%= id16_index %>>().AsRawValue();
344            literalarray_id<%= id16_index %> = GetRuntime()->ResolveOffsetByIndex(GetGraph()->GetMethod(), literalarray_id<%= id16_index %>);
345            inst->AddImm(GetGraph()->GetAllocator(), literalarray_id<%= id16_index %>);
346%         end
347%         id16_index = id16_index + 1
348%       end
349%     end
350%     if need_newtarget
351            if (!GetGraph()->IsBytecodeOptimizer()) {
352                auto input = GetDefinition(GetGraph()->GetRuntime()->GetMethodRegistersCount(GetMethod()) + 1);
353                // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
354                if constexpr (with_speculative) {
355                    input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state);
356                }
357                inst->AppendInput(input);
358                inst->AddInputType(DataType::ANY);
359            }
360%     end
361%     if is_range_call
362%           range_reg_idx = (name.include? "CREATEOBJECTWITHEXCLUDEDKEYS") ? 1 : 0
363%           imm_arg_num = has_ic_slot ? 'imm1' : 'imm0'
364%           num_actual_vregs = range_should_exclude_last ? imm_arg_num : imm_arg_num + " + 1"
365            size_t start_reg = bc_inst->GetVReg<<%= format %>, <%= range_reg_idx %>>();
366            for (uint32_t i = 1; i < <%= num_actual_vregs %>; ++i) {
367                auto input = GetDefinition(start_reg + i);
368                // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
369                if constexpr (with_speculative) {
370                    input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state);
371                }
372                inst->AppendInput(input);
373                inst->AddInputType(DataType::ANY);
374            }
375%     end
376%     if acc_read
377            {
378                auto input = GetDefinitionAcc();
379                // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
380                if constexpr (with_speculative) {
381                    input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state);
382                }
383                inst->AppendInput(input);
384                inst->AddInputType(DataType::ANY);
385                inst->SetFlag(compiler::inst_flags::ACC_READ);
386            }
387%     end
388            inst->AppendInput(inst_save_state);
389            inst->AddInputType(DataType::NO_TYPE);
390            AddInstruction(inst);
391%     if acc_write
392            UpdateDefinitionAcc(inst);
393            inst->SetFlag(compiler::inst_flags::ACC_WRITE);
394%     end
395            break;
396        }
397% end
398        default:
399            failed_ = true;
400            LOG(ERROR,COMPILER) << "unknown Ecma opcode!" << static_cast<int>(bc_inst->GetOpcode());
401            return;
402  }
403}
404}  // namespace panda::compiler
405