324 lines
No EOL
11 KiB
Ruby
324 lines
No EOL
11 KiB
Ruby
require './generator'
|
|
|
|
class ExecutorGenerator < Generator
|
|
def self.filter_key
|
|
"executor"
|
|
end
|
|
|
|
def initialize
|
|
super
|
|
@need_guest = false
|
|
@noguest_types = Set.new(["void"])
|
|
@expand_common = false
|
|
end
|
|
|
|
def need_guest
|
|
if @need_guest then
|
|
return yield
|
|
else
|
|
@need_guest = true
|
|
result = yield
|
|
@need_guest = false
|
|
return result
|
|
end
|
|
end
|
|
|
|
def type_needs_guest?(type)
|
|
size_of_type(type) != 1 && ! @noguest_types.include?(type)
|
|
end
|
|
|
|
def guestify_type(type, guest)
|
|
type.strip!
|
|
return type, "", "" if not type_needs_guest?(type)
|
|
if type =~ /^(.*)(\[[^\[\]]*\])$/ then
|
|
post = $2
|
|
type1, _, _ = guestify_type($1, true)
|
|
|
|
#if guest then
|
|
# return "GUEST<#{type1}#{post}>", "", ""
|
|
#else
|
|
return type1, "", post
|
|
#end
|
|
elsif type =~ /^(.*)\*$/ then
|
|
type1, _, _ = guestify_type($1, true)
|
|
|
|
if guest then
|
|
return "GUEST<#{type1}*>", "", ""
|
|
else
|
|
return type1, "*", ""
|
|
end
|
|
elsif guest then
|
|
if type =~ /^const[ \t]+(.*)$/ then
|
|
if type_needs_guest?($1) then
|
|
return "const GUEST<#{$1}>"
|
|
else
|
|
return type
|
|
end
|
|
else
|
|
return "GUEST<#{type}>"
|
|
end
|
|
else
|
|
return type, "", ""
|
|
end
|
|
end
|
|
|
|
def decl(type, thing=nil)
|
|
t1, pre, post = guestify_type(type, @need_guest)
|
|
|
|
return "#{t1} #{pre}#{thing}#{post}"
|
|
end
|
|
|
|
def convert_expression(expr)
|
|
return expr.gsub(/\'([^\']+)\'/) {|x| "\"#{$1}\"_4"}
|
|
end
|
|
|
|
def declare_struct_union(what, value)
|
|
@noguest_types << value["name"]
|
|
|
|
@out << "#{what} #{value["name"]}"
|
|
if value["members"] then
|
|
@out << "{"
|
|
@out << "GUEST_STRUCT;"
|
|
need_guest { declare_members(value["members"]) }
|
|
@out << "}"
|
|
end
|
|
@out << ";"
|
|
if value["size"] then
|
|
@out << "static_assert(sizeof(#{value["name"]}) == #{value["size"]});"
|
|
end
|
|
end
|
|
def declare_dispatcher(value, extern:false)
|
|
@available_dispatchers << value["name"]
|
|
@out << "EXTERN_" if extern
|
|
@out << "DISPATCHER_TRAP(#{value["name"]}, "
|
|
@out << "#{hexlit(value["trap"])}, #{value["selector-location"]});\n"
|
|
end
|
|
def declare_lowmem(value)
|
|
@out << "const LowMemGlobal<#{decl(value["type"])}> #{value["name"]}"
|
|
@out << "{ #{hexlit(value["address"],12)} };\n"
|
|
# @out << "LOWMEM_ACCESSOR(#{value["name"]});\n"
|
|
if value["type"] =~ /^(.*)\[[^\[\]]*\]$/ then
|
|
# @out << "inline GUEST<#{$1}>* LMGet#{value["name"]}() { return LM(#{value["name"]}); }\n"
|
|
# @out << "NOTRAP_FUNCTION2(LMGet#{value["name"]});\n"
|
|
else
|
|
sz = size_of_type(value["type"])
|
|
|
|
if !sz || sz > 4 then
|
|
@out << "inline void LMGet#{value["name"]}(#{decl(value["type"]+"*", "val")}) { *val = LM(#{value["name"]}); }\n"
|
|
@out << "inline void LMSet#{value["name"]}(#{decl("const " + value["type"]+"*", "val")}) { LM(#{value["name"]}) = *val; }\n"
|
|
else
|
|
@out << "inline #{decl(value["type"])} LMGet#{value["name"]}() { return LM(#{value["name"]}); }\n"
|
|
@out << "inline void LMSet#{value["name"]}(#{decl(value["type"],"val")}) { LM(#{value["name"]}) = val; }\n"
|
|
end
|
|
@out << "NOTRAP_FUNCTION2(LMGet#{value["name"]});\n"
|
|
@out << "NOTRAP_FUNCTION2(LMSet#{value["name"]});\n"
|
|
end
|
|
|
|
end
|
|
|
|
def handle_regcall_conv(fun)
|
|
args = (fun["args"] or [])
|
|
@out << (fun["returnreg"] or "void")
|
|
argregs = args.map do |arg|
|
|
basetype = $1 if arg["type"] =~ /^(.*)\* *$/
|
|
if arg["register"] =~ /^Out<(.*)>$/ then
|
|
"Out<#{basetype}, #{$1}>"
|
|
elsif arg["register"] =~ /^InOut<(.*)>$/ then
|
|
"InOut<#{basetype}, #{$1}>"
|
|
else
|
|
arg["register"]
|
|
end
|
|
end
|
|
@out << "(" << argregs.join(", ") << ")"
|
|
@out << ", " << fun["executor_extras"] if fun["executor_extras"]
|
|
end
|
|
|
|
def declare_function(fun)
|
|
if fun["dispatcher"] && ! @available_dispatchers.include?(fun["dispatcher"]) then
|
|
disp = $global_name_map[fun["dispatcher"]]
|
|
if disp and disp["dispatcher"] then
|
|
declare_dispatcher(disp["dispatcher"], extern:true)
|
|
end
|
|
end
|
|
|
|
name = fun["name"]
|
|
dispatcher = (fun["dispatcher"] && $global_name_map[fun["dispatcher"]]["dispatcher"])
|
|
trap = (fun["trap"] or (dispatcher and dispatcher["trap"]))
|
|
|
|
cname = name
|
|
if fun["executor"] then
|
|
if fun["executor"].is_a?(String) then
|
|
if fun["executor"] =~ /_$/ then
|
|
cname = fun["executor"] + name
|
|
else
|
|
cname = fun["executor"]
|
|
end
|
|
end
|
|
end
|
|
two = name == cname ? "2" : ""
|
|
|
|
@out << (fun["return"] or "void") << " " << cname
|
|
|
|
args = (fun["args"] or [])
|
|
|
|
@out << "(" << (args.map {|arg| decl(arg["type"], arg["name"])}).join(", ") << ");"
|
|
|
|
if fun["file_trap"] == "hfs" then
|
|
trap_sel_disp = hexlit(Integer(trap) & 0xA0FF)
|
|
else
|
|
trap_sel_disp = hexlit(trap)
|
|
end
|
|
if fun["selector"] then
|
|
sub = "SUB"
|
|
trap_sel_disp += ", #{hexlit(fun["selector"])}, #{hexlit(fun["dispatcher"])}"
|
|
else
|
|
sub = ""
|
|
end
|
|
|
|
if not fun["executor"] then
|
|
elsif fun["file_trap"] == "mfs" then
|
|
elsif fun["file_trap"] == "hfs" then
|
|
@out << "HFS_#{sub}TRAP(#{name.gsub(/^PBH/,"PB")}, #{name}, "
|
|
@out << "#{fun["args"][0]["type"]}, #{trap_sel_disp});"
|
|
elsif fun["file_trap"] then
|
|
@out << "FILE_#{sub}TRAP(#{name}, #{fun["args"][0]["type"]}, #{trap_sel_disp});"
|
|
elsif trap && (fun["executor_extras"] || fun["returnreg"] || args.any? {|arg| arg["register"]}) then
|
|
if fun["variants"] then
|
|
variants = fun["variants"]
|
|
nflagstr = variants.size >= 3 ? "2" : ""
|
|
@out << "REGISTER_#{nflagstr}FLAG_TRAP(#{cname}, #{variants.join(", ")}, "
|
|
@out << "#{hexlit(trap)}, "
|
|
@out << (fun["return"] or "void")
|
|
args1 = variants.size >= 3 ? fun["args"][0..-3] : fun["args"][0..-2]
|
|
@out << "(" << (args1.map {|arg| decl(arg["type"])}).join(", ") << ")"
|
|
@out << ", "
|
|
else
|
|
@out << "REGISTER_#{sub}TRAP#{two}(#{name}, #{trap_sel_disp}, "
|
|
end
|
|
handle_regcall_conv(fun)
|
|
@out << ");\n"
|
|
elsif fun["selector"] then
|
|
@out << "PASCAL_SUBTRAP(#{name}, #{hexlit(trap)}, "
|
|
@out << "#{hexlit(fun["selector"])}, #{hexlit(fun["dispatcher"])});\n"
|
|
elsif trap then
|
|
if name == cname then
|
|
if args.size == 0 && !fun["return"] then
|
|
@out << "REGISTER_TRAP2(#{name}, #{hexlit(trap)}, void());\n"
|
|
else
|
|
end
|
|
else
|
|
@out << "PASCAL_TRAP(#{name}, #{hexlit(trap)});\n"
|
|
end
|
|
else
|
|
@out << "NOTRAP_FUNCTION#{two}(#{name});\n"
|
|
end
|
|
end
|
|
|
|
def declare_funptr(value)
|
|
name = value["name"]
|
|
args = (value["args"] or [])
|
|
|
|
@out << "using #{name} = UPP<"
|
|
@out << (value["return"] or "void") << "("
|
|
args = (value["args"] or [])
|
|
@out << args.map {|arg|decl(arg["type"], arg["name"])}.join(", ")
|
|
@out << ")"
|
|
if value["returnreg"] || value["executor_extras"] || args.any? {|arg| arg["register"]} then
|
|
@out << ", Register<"
|
|
handle_regcall_conv(value)
|
|
@out << ">"
|
|
elsif value["callconv"] == "C" then
|
|
@out << ", callconv::CCall"
|
|
end
|
|
@out << ">;\n"
|
|
end
|
|
|
|
def declare_verbatim(value)
|
|
@out << "BEGIN_EXECUTOR_ONLY\n"
|
|
@out << value.strip << "\n"
|
|
@out << "END_EXECUTOR_ONLY\n"
|
|
end
|
|
|
|
def remap_name(name)
|
|
name == "MacTypes" ? "ExMacTypes" : name
|
|
end
|
|
|
|
def generate_preamble(header)
|
|
super
|
|
@out << "#pragma once\n"
|
|
#if header.name == "MacTypes" then
|
|
@out << "#include \"base/mactype.h\"\n"
|
|
@out << "#include <cassert>\n"
|
|
@out << "#include <base/lowglobals.h>\n"
|
|
#end
|
|
|
|
header.included.each do |file|
|
|
@out << "#include \"#{remap_name(file)}.h\"\n"
|
|
end
|
|
@out << "\n"
|
|
|
|
@out << "#define MODULE_NAME #{header.name}\n"
|
|
@out << "#include <base/api-module.h>\n"
|
|
|
|
@out << "\n\n"
|
|
|
|
@out << "namespace Executor {\n\n"
|
|
end
|
|
|
|
def generate_postamble(header)
|
|
@out << "} /* namespace Executor */"
|
|
end
|
|
|
|
def generate_header(header)
|
|
@current_header = header
|
|
@available_dispatchers = Set.new
|
|
super
|
|
end
|
|
|
|
def make_api_ifdef(api)
|
|
if api == "carbon" then
|
|
else
|
|
yield
|
|
end
|
|
end
|
|
|
|
def generate(defs)
|
|
print "Writing Headers...\n"
|
|
FileUtils.mkdir_p "#{$options.output_dir}/api"
|
|
FileUtils.mkdir_p "#{$options.output_dir}/trap_instances"
|
|
moduleList = []
|
|
defs.topsort.each do |name|
|
|
name2 = remap_name(name)
|
|
moduleList << name2
|
|
formatted_file "#{$options.output_dir}/api/#{name2}.h" do |f|
|
|
f << generate_header(defs.headers[name])
|
|
end
|
|
formatted_file "#{$options.output_dir}/trap_instances/#{name2}.cpp" do |f|
|
|
f << <<END
|
|
#define INSTANTIATE_TRAPS_#{name2}
|
|
#include <#{name2}.h>
|
|
|
|
// Function for preventing the linker from considering the static constructors in this module unused
|
|
namespace Executor::ReferenceTraps {
|
|
void #{name2}() {}
|
|
}
|
|
END
|
|
end
|
|
end
|
|
formatted_file "#{$options.output_dir}/trap_instances/ReferenceAllTraps.cpp" do |f|
|
|
f << "namespace Executor {\n"
|
|
f << "namespace ReferenceTraps {\n"
|
|
moduleList.each { |m| f << "void #{m}();\n" }
|
|
f << "}\nvoid ReferenceAllTraps() {\n"
|
|
moduleList.each { |m| f << "ReferenceTraps::#{m}();\n" }
|
|
f << "}\n}\n"
|
|
end
|
|
File.open "#{$options.output_dir}/trap_instances/trap_instances.cmake", "w" do |f|
|
|
f << "set(trap_instance_sources\n"
|
|
f << " \"#{$options.output_dir}/trap_instances/ReferenceAllTraps.cpp\"\n"
|
|
moduleList.each { |m| f << " \"#{$options.output_dir}/trap_instances/#{m}.cpp\"\n" }
|
|
f << ")\n"
|
|
end
|
|
print "Done.\n"
|
|
end
|
|
end |