1#!/usr/bin/env ruby 2 3# Copyright (c) 2021-2024 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16require 'optparse' 17require 'ostruct' 18require 'yaml' 19 20Options = OpenStruct.new 21class << Options 22 23 attr_accessor :compiling 24 25 def parse 26 OptionParser.new do |opts| 27 opts.banner = "Usage: irtoc.rb [options] INPUT_FILE" 28 29 opts.on("--ark_source_dir=PATH", "Path to panda source code") { |v| self.ark_source_dir = v } 30 opts.on("--output=PATH", "Output file") { |v| self.output_files = v.split(':') } 31 opts.on("--input=PATH", "Input files, separated by ':' symbol") { |v| self.input_files = v.split(':') } 32 opts.on('-D=STR', 'C++ definitions that will be used for compiling output file') { |v| (self.definitions ||= []) << v } 33 opts.on("--arch=ARCH", "Target architecture") { |v| self.arch = v } 34 opts.on('--cpu-features=FEATURE,FEATURE', 'Supported CPU Features') { |v| self.cpu_features = v.split(',').map { |f| f.to_sym }} 35 opts.on('--ir-api=API', 'API to emit during C++ code generation') { |v| self.ir_api = v } 36 opts.on('--isa=PATH', 'ISA YAML file') { |v| self.isa = v } 37 opts.on('--plugins=PATH', 'Plugins file') { |v| self.plugins = v } 38 opts.on('--working-dir=PATH', 'Run in specified working directory') { |v| self.working_dir = v } 39 end.parse! 40 41 if Options.working_dir 42 Dir.mkdir(Options.working_dir) unless File.exists?(Options.working_dir) 43 Dir.chdir(Options.working_dir) 44 end 45 46 self.instructions_yaml = "#{self.ark_source_dir}/compiler/optimizer/ir/instructions.yaml" 47 self.isapi = "#{self.ark_source_dir}/isa/isapi.rb" 48 self.commonapi = "#{self.ark_source_dir}/templates/common.rb" 49 50 # Collect compiler definitions 51 if self.definitions 52 new_definitions = OpenStruct.new 53 self.definitions.each do |define| 54 vals = define.split('=') 55 new_definitions[vals[0]] = vals.size > 1 ? vals[1] : true 56 end 57 self.definitions = new_definitions 58 else 59 self.definitions = OpenStruct.new 60 end 61 self.definitions.DEBUG = !self.definitions.NDEBUG 62 63 # Collect plugins files 64 if self.plugins 65 new_plugins = Hash.new { |h, k| h[k] = [] } 66 File.open(self.plugins).readlines.each do |plugin| 67 next if plugin.strip.empty? 68 full_plugin_path = "#{self.ark_source_dir}/#{plugin}".chop 69 line_matches = File.open(full_plugin_path).to_a.first.match(/# +plugin +(.*)/) 70 raise "Irtoc plugin doesn't specifiy its name: #{full_plugin_path}" unless line_matches 71 plugin_name = line_matches[1] 72 new_plugins[plugin_name] << full_plugin_path 73 end 74 self.plugins = new_plugins 75 else 76 self.plugins = Hash.new { |h, k| h[k] = [] } 77 end 78 79 # Determine target architecture 80 if self.arch.nil? 81 arch_str = self.definitions.marshal_dump.keys.grep(/PANDA_TARGET_(AMD64|ARM64|ARM32)/)[0] 82 self.arch = arch_str.to_s.sub('PANDA_TARGET_','').downcase 83 end 84 self.arch = self.arch.to_sym 85 if self.arch == :amd64 || self.arch == :x64 86 self.arch = :x86_64 87 end 88 if self.arch == :arm 89 self.arch = :arm32 90 end 91 possible_arch = %w[arm64 arm32 x86_64 x86] 92 raise "Wrong arch: #{arch_str}" unless possible_arch.include?(self.arch.to_s) 93 94 # Read compiler arch info 95 arch_info = YAML.load_file("#{self.ark_source_dir}/compiler/optimizer/ir/instructions.yaml")['arch_info'] 96 raise "Compiler config doesn't contain `arch_info`" unless arch_info 97 arch_info = arch_info[arch_info.index { |x| x['name'].to_sym == self.arch }] 98 raise "Arch info not found for #{self.arch}" unless arch_info 99 self.arch_info = OpenStruct.new(arch_info) 100 101 # Determine CPU features. Notice: default-enabled CPU features ('sse42') must be reflected here. 102 # See the 'compiler/compiler.yaml' file. 103 if self.cpu_features.nil? 104 self.cpu_features = [:sse42] 105 end 106 possible_features = %w[none crc32 sse42 jscvt atomics] 107 self.cpu_features.each do |feature| 108 raise "Wrong cpu feature: #{feature}" unless possible_features.include?(feature.to_s) 109 end 110 111 raise 'Supported IR APIs: ir-constructor, ir-builder, ir-inline' unless self.ir_api =~ /^ir-(constructor|builder|inline)$/ 112 end 113 114 def arch_64_bits? 115 self.arch == :x86_64 || self.arch == :arm64 116 end 117 118 def arm64? 119 self.arch == :arm64 120 end 121 122 def cpp_arch 123 @cpp_arch_map ||= { 124 arm32: "Arch::AARCH32", 125 arm64: "Arch::AARCH64", 126 x86_64: "Arch::X86_64" 127 } 128 @cpp_arch_map[self.arch] 129 end 130 131 def crc32_feature_enabled? 132 self.cpu_features.include?(:crc32) 133 end 134 135 def sse42_feature_enabled? 136 self.cpu_features.include?(:sse42) 137 end 138 139 def jscvt_feature_enabled? 140 self.cpu_features.include?(:jscvt) 141 end 142 143 def atomics_feature_enabled? 144 self.cpu_features.include?(:atomics) 145 end 146 147end 148