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