#!/usr/bin/env ruby # Copyright (c) 2021-2022 Huawei Device Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'optparse' require 'ostruct' require 'yaml' Options = OpenStruct.new class << Options attr_accessor :compiling def parse OptionParser.new do |opts| opts.banner = "Usage: irtoc.rb [options] INPUT_FILE" opts.on("--ark_source_dir=PATH", "Path to panda source code") { |v| self.ark_source_dir = v } opts.on("--output=PATH", "Output file") { |v| self.output_file = v } opts.on("--input=PATH", "Input files, separated by ':' symbol") { |v| self.input_files = v.split(':') } opts.on("--definitions=LIST", "C++ definitions that will be used for compiling output file") { |v| self.definitions = v } opts.on("--arch=ARCH", "Target architecture") { |v| self.arch = v } opts.on('--ir-api=API', 'API to emit during C++ code generation') { |v| self.ir_api = v } opts.on('--isa=PATH', 'ISA YAML file') { |v| self.isa = v } opts.on('--plugins=PATH', 'Plugins file') { |v| self.plugins = v } opts.on('--keep-artifacts', 'Do not remove intermediate files') { |v| self.keep_artifacts = true } end.parse! self.instructions_yaml = "#{self.ark_source_dir}/compiler/optimizer/ir/instructions.yaml" self.isapi = "#{self.ark_source_dir}/isa/isapi.rb" self.commonapi = "#{self.ark_source_dir}/templates/common.rb" # Collect compiler definitions if self.definitions defines = self.definitions.split(':') new_definitions = OpenStruct.new defines.each do |define| vals = define.split('=') new_definitions[vals[0]] = vals.size > 1 ? vals[1] : true end self.definitions = new_definitions else self.definitions = OpenStruct.new end self.definitions.DEBUG = !self.definitions.NDEBUG # Collect plugins files if self.plugins new_plugins = Hash.new { |h, k| h[k] = [] } File.open(self.plugins).readlines.each do |plugin| next if plugin.strip.empty? full_plugin_path = "#{self.ark_source_dir}/#{plugin}".chop line_matches = File.open(full_plugin_path).to_a.first.match(/# +plugin +(.*)/) raise "Irtoc plugin doesn't specifiy its name: #{full_plugin_path}" unless line_matches plugin_name = line_matches[1] new_plugins[plugin_name] << full_plugin_path end self.plugins = new_plugins else self.plugins = Hash.new { |h, k| h[k] = [] } end # Determine target architecture if self.arch.nil? arch_str = self.definitions.marshal_dump.keys.grep(/PANDA_TARGET_(AMD64|ARM64|ARM32)/)[0] self.arch = arch_str.to_s.sub('PANDA_TARGET_','').downcase end self.arch = self.arch.to_sym if self.arch == :amd64 || self.arch == :x64 self.arch = :x86_64 end if self.arch == :arm self.arch = :arm32 end possible_arch = %w[arm64 arm32 x86_64 x86] raise "Wrong arch: #{arch_str}" unless possible_arch.include?(self.arch.to_s) # Read compiler arch info arch_info = YAML.load_file("#{self.ark_source_dir}/compiler/optimizer/ir/instructions.yaml")['arch_info'] raise "Compiler config doesn't contain `arch_info`" unless arch_info arch_info = arch_info[arch_info.index { |x| x['name'].to_sym == self.arch }] raise "Arch info not found for #{self.arch}" unless arch_info self.arch_info = OpenStruct.new(arch_info) raise 'Supported IR APIs: ir-constructor, ir-builder, ir-inline' unless self.ir_api =~ /^ir-(constructor|builder|inline)$/ end def arch_64_bits? self.arch == :x86_64 || self.arch == :arm64 end def arm64? self.arch == :arm64 end def cpp_arch @cpp_arch_map ||= { arm32: "Arch::AARCH32", arm64: "Arch::AARCH64", x86_64: "Arch::X86_64" } @cpp_arch_map[self.arch] end end