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('--ir-api=API', 'API to emit during C++ code generation') { |v| self.ir_api = v }
35      opts.on('--isa=PATH', 'ISA YAML file') { |v| self.isa = v }
36      opts.on('--plugins=PATH', 'Plugins file') { |v| self.plugins = v }
37      opts.on('--working-dir=PATH', 'Run in specified working directory') { |v| self.working_dir = v }
38    end.parse!
39
40    if Options.working_dir
41      Dir.mkdir(Options.working_dir) unless File.exists?(Options.working_dir)
42      Dir.chdir(Options.working_dir)
43    end
44
45    self.instructions_yaml = "#{self.ark_source_dir}/compiler/optimizer/ir/instructions.yaml"
46    self.isapi = "#{self.ark_source_dir}/isa/isapi.rb"
47    self.commonapi = "#{self.ark_source_dir}/templates/common.rb"
48
49    # Collect compiler definitions
50    if self.definitions
51      new_definitions = OpenStruct.new
52      self.definitions.each do |define|
53        vals = define.split('=')
54        new_definitions[vals[0]] = vals.size > 1 ? vals[1] : true
55      end
56      self.definitions = new_definitions
57    else
58      self.definitions = OpenStruct.new
59    end
60    self.definitions.DEBUG = !self.definitions.NDEBUG
61
62    # Collect plugins files
63    if self.plugins
64      new_plugins = Hash.new { |h, k| h[k] = [] }
65      File.open(self.plugins).readlines.each do |plugin|
66        next if plugin.strip.empty?
67        full_plugin_path = "#{self.ark_source_dir}/#{plugin}".chop
68        line_matches = File.open(full_plugin_path).to_a.first.match(/# +plugin +(.*)/)
69        raise "Irtoc plugin doesn't specifiy its name: #{full_plugin_path}" unless line_matches
70        plugin_name = line_matches[1]
71        new_plugins[plugin_name] << full_plugin_path
72      end
73      self.plugins = new_plugins
74    else
75      self.plugins = Hash.new { |h, k| h[k] = [] }
76    end
77
78    # Determine target architecture
79    if self.arch.nil?
80      arch_str = self.definitions.marshal_dump.keys.grep(/PANDA_TARGET_(AMD64|ARM64|ARM32)/)[0]
81      self.arch = arch_str.to_s.sub('PANDA_TARGET_','').downcase
82    end
83    self.arch = self.arch.to_sym
84    if self.arch == :amd64 || self.arch == :x64
85      self.arch = :x86_64
86    end
87    if self.arch == :arm
88      self.arch = :arm32
89    end
90    possible_arch = %w[arm64 arm32 x86_64 x86]
91    raise "Wrong arch: #{arch_str}" unless possible_arch.include?(self.arch.to_s)
92
93    # Read compiler arch info
94    arch_info = YAML.load_file("#{self.ark_source_dir}/compiler/optimizer/ir/instructions.yaml")['arch_info']
95    raise "Compiler config doesn't contain `arch_info`" unless arch_info
96    arch_info = arch_info[arch_info.index { |x| x['name'].to_sym == self.arch }]
97    raise "Arch info not found for #{self.arch}" unless arch_info
98    self.arch_info = OpenStruct.new(arch_info)
99
100    raise 'Supported IR APIs: ir-constructor, ir-builder, ir-inline' unless self.ir_api =~ /^ir-(constructor|builder|inline)$/
101  end
102
103  def arch_64_bits?
104    self.arch == :x86_64 || self.arch == :arm64
105  end
106
107  def arm64?
108    self.arch == :arm64
109  end
110
111  def cpp_arch
112    @cpp_arch_map ||= {
113      arm32: "Arch::AARCH32",
114      arm64: "Arch::AARCH64",
115      x86_64: "Arch::X86_64"
116    }
117    @cpp_arch_map[self.arch]
118  end
119
120end
121