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