• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env ruby
2
3# Copyright (c) 2021-2022 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_relative 'options'
17# Parse options in advance, since it may be required during scripts loading
18Options.parse
19
20require_relative 'function'
21require_relative 'cpp_function'
22require_relative 'instructions_data'
23require_relative 'output'
24require_relative 'ir_generator'
25require_relative 'isa'
26require 'optparse'
27require 'tempfile'
28require 'yaml'
29
30FILE_BEGIN = %{
31// THIS FILE WAS GENERATED FOR #{Options.arch.upcase}
32
33#include "irtoc/backend/compilation_unit.h"
34
35#ifndef __clang_analyzer__
36
37using namespace panda::compiler;
38
39namespace panda::irtoc \{
40
41}
42
43FILE_END = %{
44\} // namespace panda::irtoc
45
46#endif  // __clang_analyzer__
47}
48
49class Irtoc
50
51  attr_reader :functions, :cpp_functions
52
53  def initialize
54    # List of all compiled functions.
55    @functions = []
56    # Currently processed script file.
57    @current_script = nil
58    # Macros, that are defined for the current script. Since each macros is just a method in Function class, we need
59    # to remove all these methods after script is done. Because further scripts should not see macros, defined in
60    # previous one.
61    @macros = []
62    # Macros that also generate corresponding IR Builder methods
63    @builder_macros = []
64  end
65
66  (0..31).each { |i| define_method("r#{i}") { i } }
67  { :rax => 0,
68    :rcx => 1,
69    :rdx => 2,
70    :rbx => 11,
71    :rsp => 10,
72    :rbp => 9,
73    :rsi => 6,
74    :rdi => 7}.each { |name, value| define_method(name) { value } }
75
76  def function(name, **kwargs, &block)
77    func = Function.new name, **kwargs, &block
78    @functions << func unless func.mode.nil?
79    func.compile
80  end
81
82  def cpp_function(name, &block)
83    func = CppFunction.new(name)
84    func.instance_eval(&block)
85    (@cpp_functions ||= []) << func
86  end
87
88  def macro(name, &block)
89    raise "Macro can't start with capital letter" if name.to_s.start_with? /[A-Z]/
90    Function.define_method(name.to_sym, &block)
91    @macros << name
92  end
93
94  def remove_macros
95    @macros.each do |name|
96      Function.remove_method name
97    end
98    @macros.clear
99  end
100
101  # Since Irtoc can be executed in parallel for the same files and from same working directory, we need to separate
102  # generated files between these parallel runs
103  def uniq_filename(base)
104    num = (rand() * 100000).round.to_s(16)
105    name = "#{base}.#{num}"
106    begin
107      yield name
108    ensure
109      File.delete(name) unless Options.keep_artifacts
110    end
111  end
112
113  def include_relative(filename)
114    uniq_filename("#{File.basename(filename)}.fixed") do |fixed_filename|
115      filename = "#{File.dirname(@current_script)}/#{filename}"
116      preprocess(filename, fixed_filename)
117      self.instance_eval File.open(fixed_filename).read, filename
118    end
119  end
120
121  def preprocess_line(line)
122    line.gsub(/(\w+) *:= *([^>])/, 'let :\1, \2').gsub(/\} *Else/, "}; Else").gsub(/%(\w+)/, 'LiveIn(regmap[:\1])')
123  end
124
125  def read_plugin(filename)
126    return "" if Options.plugins.empty?
127    output = ""
128    Options.plugins[filename].each do |full_filename|
129      File.open(full_filename).readlines.each do |line|
130        output += preprocess_line(line)
131      end
132    end
133    output
134  end
135
136  def preprocess(filename, output_filename)
137    File.open(output_filename, "w") do | file|
138      File.open(filename).readlines.each do |line|
139        line_matches = line.match(/include_plugin '(.*)'/)
140        if line_matches
141          filename = line_matches.captures[0]
142          file.puts read_plugin(filename)
143        else
144          file.puts preprocess_line(line)
145        end
146      end
147    end
148  end
149
150  def run(input_file)
151    @current_script = input_file
152    uniq_filename("#{File.basename(input_file)}.fixed") do |fixed_filename|
153      preprocess(input_file, fixed_filename)
154      begin
155        data = File.open(fixed_filename).read
156        self.instance_eval data, fixed_filename
157      rescue SyntaxError => e
158        puts "========== Begin #{fixed_filename} =========="
159        puts data
160        puts "========== End #{fixed_filename} =========="
161        raise
162      end
163    end
164  end
165end
166
167def main
168  abort "YAML description file is not specified" unless Options.instructions_yaml
169  abort "ISA YAML file is not specified" unless Options.isa
170  abort "ISAPI file is not specified" unless Options.isapi
171
172  InstructionsData.setup Options.instructions_yaml
173  Function.setup
174  IRInstruction.setup
175  ISA.setup Options.isa, Options.isapi
176
177  require Options.commonapi
178
179  functions = []
180  cpp_functions = []
181
182  Options.input_files.each do |input_file|
183    irtoc = Irtoc.new
184    irtoc.run(input_file)
185    functions += irtoc.functions
186    cpp_functions += irtoc.cpp_functions if irtoc.cpp_functions
187  end
188
189  if Options.ir_api == 'ir-constructor'
190    Output.setup Options.output_file
191    Output.println(FILE_BEGIN)
192    functions.reject(&:enable_builder).each(&:generate)
193    Output.println(FILE_END)
194  elsif Options.ir_api == 'ir-builder'
195    builder_functions = functions.select(&:enable_builder)
196    Output.setup Options.output_file
197    Output.println('#include "optimizer/ir/graph.h"')
198    Output.println('#include "optimizer/ir/basicblock.h"')
199    Output.println('#include "optimizer/ir/inst.h"')
200    Output.println('#include "asm_defines.h"')
201    Output.println('namespace panda::compiler {')
202    builder_functions.each(&:generate_builder)
203    Output.println('} // namespace panda::compiler')
204  elsif Options.ir_api == 'ir-inline'
205    Output.setup Options.output_file
206    Output << '#include "optimizer/ir/graph.h"'
207    Output << '#include "optimizer/ir/basicblock.h"'
208    Output << '#include "optimizer/ir/inst.h"'
209    Output << "#include \"asm_defines.h\"\n"
210    Output << "namespace panda::compiler {\n"
211    cpp_functions.each { |func| func.generate_ir(GeneratorIrInline.new) }
212    Output << '}  // namespace panda::compiler'
213  else
214    abort 'Should be unreachable: Unknown IR API'
215  end
216
217  validation = Hash[functions.select(&:validation).map { |f| [f.name, f.validation] } ]
218  File.open("validation.yaml", "w") do |file|
219    file.write(validation.to_yaml)
220  end
221end
222
223main
224