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