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