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