mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
Merge pull request #19122 from ziglang/lazy-aro
make aro-based translate-c lazily built from source
This commit is contained in:
commit
f5aad47287
68 changed files with 16578 additions and 22667 deletions
|
|
@ -643,11 +643,8 @@ set(ZIG_STAGE2_SOURCES
|
|||
"${CMAKE_SOURCE_DIR}/src/target.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/tracy.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/translate_c.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/type.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/stubs/aro_names.zig"
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
|
|
@ -822,18 +819,7 @@ set(BUILD_ZIG2_ARGS
|
|||
--dep "aro"
|
||||
--mod "root" "src/main.zig"
|
||||
--mod "build_options" "${ZIG_CONFIG_ZIG_OUT}"
|
||||
--mod "aro_options" "src/stubs/aro_options.zig"
|
||||
--mod "Builtins/Builtin.def" "src/stubs/aro_builtins.zig"
|
||||
--mod "Attribute/names.def" "src/stubs/aro_names.zig"
|
||||
--mod "Diagnostics/messages.def" "src/stubs/aro_messages.zig"
|
||||
--dep "build_options=aro_options"
|
||||
--mod "aro_backend" "deps/aro/backend.zig"
|
||||
--dep "Builtins/Builtin.def"
|
||||
--dep "Attribute/names.def"
|
||||
--dep "Diagnostics/messages.def"
|
||||
--dep "build_options=aro_options"
|
||||
--dep "backend=aro_backend"
|
||||
--mod "aro" "deps/aro/aro.zig"
|
||||
--mod "aro" "lib/compiler/aro/aro.zig"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
|
|
|
|||
16
bootstrap.c
16
bootstrap.c
|
|
@ -156,22 +156,8 @@ int main(int argc, char **argv) {
|
|||
"--dep", "build_options",
|
||||
"--dep", "aro",
|
||||
"--mod", "root", "src/main.zig",
|
||||
|
||||
"--mod", "build_options", "config.zig",
|
||||
"--mod", "aro_options", "src/stubs/aro_options.zig",
|
||||
"--mod", "Builtins/Builtin.def", "src/stubs/aro_builtins.zig",
|
||||
"--mod", "Attribute/names.def", "src/stubs/aro_names.zig",
|
||||
"--mod", "Diagnostics/messages.def", "src/stubs/aro_messages.zig",
|
||||
|
||||
"--dep", "build_options=aro_options",
|
||||
"--mod", "aro_backend", "deps/aro/backend.zig",
|
||||
|
||||
"--dep", "Builtins/Builtin.def",
|
||||
"--dep", "Attribute/names.def",
|
||||
"--dep", "Diagnostics/messages.def",
|
||||
"--dep", "build_options=aro_options",
|
||||
"--dep", "backend=aro_backend",
|
||||
"--mod", "aro", "deps/aro/aro.zig",
|
||||
"--mod", "aro", "lib/compiler/aro/aro.zig",
|
||||
NULL,
|
||||
};
|
||||
print_and_run(child_argv);
|
||||
|
|
|
|||
29
build.zig
29
build.zig
|
|
@ -8,7 +8,6 @@ const io = std.io;
|
|||
const fs = std.fs;
|
||||
const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
|
||||
const assert = std.debug.assert;
|
||||
const GenerateDef = @import("deps/aro/build/GenerateDef.zig");
|
||||
|
||||
const zig_version = std.SemanticVersion{ .major = 0, .minor = 12, .patch = 0 };
|
||||
const stack_size = 32 * 1024 * 1024;
|
||||
|
|
@ -636,34 +635,22 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St
|
|||
});
|
||||
exe.stack_size = stack_size;
|
||||
|
||||
const aro_options = b.addOptions();
|
||||
aro_options.addOption([]const u8, "version_str", "aro-zig");
|
||||
const aro_options_module = aro_options.createModule();
|
||||
const aro_backend = b.createModule(.{
|
||||
.root_source_file = .{ .path = "deps/aro/backend.zig" },
|
||||
.imports = &.{.{
|
||||
.name = "build_options",
|
||||
.module = aro_options_module,
|
||||
}},
|
||||
});
|
||||
const aro_module = b.createModule(.{
|
||||
.root_source_file = .{ .path = "deps/aro/aro.zig" },
|
||||
.root_source_file = .{ .path = "lib/compiler/aro/aro.zig" },
|
||||
});
|
||||
|
||||
const aro_translate_c_module = b.createModule(.{
|
||||
.root_source_file = .{ .path = "lib/compiler/aro_translate_c.zig" },
|
||||
.imports = &.{
|
||||
.{
|
||||
.name = "build_options",
|
||||
.module = aro_options_module,
|
||||
.name = "aro",
|
||||
.module = aro_module,
|
||||
},
|
||||
.{
|
||||
.name = "backend",
|
||||
.module = aro_backend,
|
||||
},
|
||||
GenerateDef.create(b, .{ .name = "Builtins/Builtin.def", .src_prefix = "deps/aro/aro" }),
|
||||
GenerateDef.create(b, .{ .name = "Attribute/names.def", .src_prefix = "deps/aro/aro" }),
|
||||
GenerateDef.create(b, .{ .name = "Diagnostics/messages.def", .src_prefix = "deps/aro/aro", .kind = .named }),
|
||||
},
|
||||
});
|
||||
|
||||
exe.root_module.addImport("aro", aro_module);
|
||||
exe.root_module.addImport("aro_translate_c", aro_translate_c_module);
|
||||
return exe;
|
||||
}
|
||||
|
||||
|
|
|
|||
431
deps/aro/aro/Attribute/names.def
vendored
431
deps/aro/aro/Attribute/names.def
vendored
|
|
@ -1,431 +0,0 @@
|
|||
# multiple
|
||||
deprecated
|
||||
.tag = .deprecated
|
||||
.c23 = true
|
||||
.gnu = true
|
||||
.declspec = true
|
||||
|
||||
fallthrough
|
||||
.tag = .fallthrough
|
||||
.c23 = true
|
||||
.gnu = true
|
||||
|
||||
noreturn
|
||||
.tag = .@"noreturn"
|
||||
.c23 = true
|
||||
.gnu = true
|
||||
.declspec = true
|
||||
|
||||
no_sanitize_address
|
||||
.tag = .no_sanitize_address
|
||||
.gnu = true
|
||||
.declspec = true
|
||||
|
||||
noinline
|
||||
.tag = .@"noinline"
|
||||
.gnu = true
|
||||
.declspec = true
|
||||
|
||||
# c23 only
|
||||
nodiscard
|
||||
.tag = .nodiscard
|
||||
.c23 = true
|
||||
|
||||
reproducible
|
||||
.tag = .reproducible
|
||||
.c23 = true
|
||||
|
||||
unsequenced
|
||||
.tag = .unsequenced
|
||||
.c23 = true
|
||||
|
||||
maybe_unused
|
||||
.tag = .unused
|
||||
.c23 = true
|
||||
|
||||
# gnu only
|
||||
access
|
||||
.tag = .access
|
||||
.gnu = true
|
||||
|
||||
alias
|
||||
.tag = .alias
|
||||
.gnu = true
|
||||
|
||||
aligned
|
||||
.tag = .aligned
|
||||
.gnu = true
|
||||
|
||||
alloc_align
|
||||
.tag = .alloc_align
|
||||
.gnu = true
|
||||
|
||||
alloc_size
|
||||
.tag = .alloc_size
|
||||
.gnu = true
|
||||
|
||||
always_inline
|
||||
.tag = .always_inline
|
||||
.gnu = true
|
||||
|
||||
artificial
|
||||
.tag = .artificial
|
||||
.gnu = true
|
||||
|
||||
assume_aligned
|
||||
.tag = .assume_aligned
|
||||
.gnu = true
|
||||
|
||||
cleanup
|
||||
.tag = .cleanup
|
||||
.gnu = true
|
||||
|
||||
cold
|
||||
.tag = .cold
|
||||
.gnu = true
|
||||
|
||||
common
|
||||
.tag = .common
|
||||
.gnu = true
|
||||
|
||||
const
|
||||
.tag = .@"const"
|
||||
.gnu = true
|
||||
|
||||
constructor
|
||||
.tag = .constructor
|
||||
.gnu = true
|
||||
|
||||
copy
|
||||
.tag = .copy
|
||||
.gnu = true
|
||||
|
||||
designated_init
|
||||
.tag = .designated_init
|
||||
.gnu = true
|
||||
|
||||
destructor
|
||||
.tag = .destructor
|
||||
.gnu = true
|
||||
|
||||
error
|
||||
.tag = .@"error"
|
||||
.gnu = true
|
||||
|
||||
externally_visible
|
||||
.tag = .externally_visible
|
||||
.gnu = true
|
||||
|
||||
flatten
|
||||
.tag = .flatten
|
||||
.gnu = true
|
||||
|
||||
format
|
||||
.tag = .format
|
||||
.gnu = true
|
||||
|
||||
format_arg
|
||||
.tag = .format_arg
|
||||
.gnu = true
|
||||
|
||||
gnu_inline
|
||||
.tag = .gnu_inline
|
||||
.gnu = true
|
||||
|
||||
hot
|
||||
.tag = .hot
|
||||
.gnu = true
|
||||
|
||||
ifunc
|
||||
.tag = .ifunc
|
||||
.gnu = true
|
||||
|
||||
interrupt
|
||||
.tag = .interrupt
|
||||
.gnu = true
|
||||
|
||||
interrupt_handler
|
||||
.tag = .interrupt_handler
|
||||
.gnu = true
|
||||
|
||||
leaf
|
||||
.tag = .leaf
|
||||
.gnu = true
|
||||
|
||||
malloc
|
||||
.tag = .malloc
|
||||
.gnu = true
|
||||
|
||||
may_alias
|
||||
.tag = .may_alias
|
||||
.gnu = true
|
||||
|
||||
mode
|
||||
.tag = .mode
|
||||
.gnu = true
|
||||
|
||||
no_address_safety_analysis
|
||||
.tag = .no_address_safety_analysis
|
||||
.gnu = true
|
||||
|
||||
no_icf
|
||||
.tag = .no_icf
|
||||
.gnu = true
|
||||
|
||||
no_instrument_function
|
||||
.tag = .no_instrument_function
|
||||
.gnu = true
|
||||
|
||||
no_profile_instrument_function
|
||||
.tag = .no_profile_instrument_function
|
||||
.gnu = true
|
||||
|
||||
no_reorder
|
||||
.tag = .no_reorder
|
||||
.gnu = true
|
||||
|
||||
no_sanitize
|
||||
.tag = .no_sanitize
|
||||
.gnu = true
|
||||
|
||||
no_sanitize_coverage
|
||||
.tag = .no_sanitize_coverage
|
||||
.gnu = true
|
||||
|
||||
no_sanitize_thread
|
||||
.tag = .no_sanitize_thread
|
||||
.gnu = true
|
||||
|
||||
no_sanitize_undefined
|
||||
.tag = .no_sanitize_undefined
|
||||
.gnu = true
|
||||
|
||||
no_split_stack
|
||||
.tag = .no_split_stack
|
||||
.gnu = true
|
||||
|
||||
no_stack_limit
|
||||
.tag = .no_stack_limit
|
||||
.gnu = true
|
||||
|
||||
no_stack_protector
|
||||
.tag = .no_stack_protector
|
||||
.gnu = true
|
||||
|
||||
noclone
|
||||
.tag = .noclone
|
||||
.gnu = true
|
||||
|
||||
nocommon
|
||||
.tag = .nocommon
|
||||
.gnu = true
|
||||
|
||||
noinit
|
||||
.tag = .noinit
|
||||
.gnu = true
|
||||
|
||||
noipa
|
||||
.tag = .noipa
|
||||
.gnu = true
|
||||
|
||||
# nonnull
|
||||
# .tag = .nonnull
|
||||
# .gnu = true
|
||||
|
||||
nonstring
|
||||
.tag = .nonstring
|
||||
.gnu = true
|
||||
|
||||
noplt
|
||||
.tag = .noplt
|
||||
.gnu = true
|
||||
|
||||
# optimize
|
||||
# .tag = .optimize
|
||||
# .gnu = true
|
||||
|
||||
packed
|
||||
.tag = .@"packed"
|
||||
.gnu = true
|
||||
|
||||
patchable_function_entry
|
||||
.tag = .patchable_function_entry
|
||||
.gnu = true
|
||||
|
||||
persistent
|
||||
.tag = .persistent
|
||||
.gnu = true
|
||||
|
||||
pure
|
||||
.tag = .pure
|
||||
.gnu = true
|
||||
|
||||
retain
|
||||
.tag = .retain
|
||||
.gnu = true
|
||||
|
||||
returns_nonnull
|
||||
.tag = .returns_nonnull
|
||||
.gnu = true
|
||||
|
||||
returns_twice
|
||||
.tag = .returns_twice
|
||||
.gnu = true
|
||||
|
||||
scalar_storage_order
|
||||
.tag = .scalar_storage_order
|
||||
.gnu = true
|
||||
|
||||
section
|
||||
.tag = .section
|
||||
.gnu = true
|
||||
|
||||
sentinel
|
||||
.tag = .sentinel
|
||||
.gnu = true
|
||||
|
||||
simd
|
||||
.tag = .simd
|
||||
.gnu = true
|
||||
|
||||
stack_protect
|
||||
.tag = .stack_protect
|
||||
.gnu = true
|
||||
|
||||
symver
|
||||
.tag = .symver
|
||||
.gnu = true
|
||||
|
||||
target
|
||||
.tag = .target
|
||||
.gnu = true
|
||||
|
||||
target_clones
|
||||
.tag = .target_clones
|
||||
.gnu = true
|
||||
|
||||
tls_model
|
||||
.tag = .tls_model
|
||||
.gnu = true
|
||||
|
||||
transparent_union
|
||||
.tag = .transparent_union
|
||||
.gnu = true
|
||||
|
||||
unavailable
|
||||
.tag = .unavailable
|
||||
.gnu = true
|
||||
|
||||
uninitialized
|
||||
.tag = .uninitialized
|
||||
.gnu = true
|
||||
|
||||
unused
|
||||
.tag = .unused
|
||||
.gnu = true
|
||||
|
||||
used
|
||||
.tag = .used
|
||||
.gnu = true
|
||||
|
||||
vector_size
|
||||
.tag = .vector_size
|
||||
.gnu = true
|
||||
|
||||
visibility
|
||||
.tag = .visibility
|
||||
.gnu = true
|
||||
|
||||
warn_if_not_aligned
|
||||
.tag = .warn_if_not_aligned
|
||||
.gnu = true
|
||||
|
||||
warn_unused_result
|
||||
.tag = .warn_unused_result
|
||||
.gnu = true
|
||||
|
||||
warning
|
||||
.tag = .warning
|
||||
.gnu = true
|
||||
|
||||
weak
|
||||
.tag = .weak
|
||||
.gnu = true
|
||||
|
||||
weakref
|
||||
.tag = .weakref
|
||||
.gnu = true
|
||||
|
||||
zero_call_used_regs
|
||||
.tag = .zero_call_used_regs
|
||||
.gnu = true
|
||||
|
||||
# declspec only
|
||||
align
|
||||
.tag = .aligned
|
||||
.declspec = true
|
||||
|
||||
allocate
|
||||
.tag = .allocate
|
||||
.declspec = true
|
||||
|
||||
allocator
|
||||
.tag = .allocator
|
||||
.declspec = true
|
||||
|
||||
appdomain
|
||||
.tag = .appdomain
|
||||
.declspec = true
|
||||
|
||||
code_seg
|
||||
.tag = .code_seg
|
||||
.declspec = true
|
||||
|
||||
dllexport
|
||||
.tag = .dllexport
|
||||
.declspec = true
|
||||
|
||||
dllimport
|
||||
.tag = .dllimport
|
||||
.declspec = true
|
||||
|
||||
jitintrinsic
|
||||
.tag = .jitintrinsic
|
||||
.declspec = true
|
||||
|
||||
naked
|
||||
.tag = .naked
|
||||
.declspec = true
|
||||
|
||||
noalias
|
||||
.tag = .@"noalias"
|
||||
.declspec = true
|
||||
|
||||
process
|
||||
.tag = .process
|
||||
.declspec = true
|
||||
|
||||
restrict
|
||||
.tag = .restrict
|
||||
.declspec = true
|
||||
|
||||
safebuffers
|
||||
.tag = .safebuffers
|
||||
.declspec = true
|
||||
|
||||
selectany
|
||||
.tag = .selectany
|
||||
.declspec = true
|
||||
|
||||
spectre
|
||||
.tag = .spectre
|
||||
.declspec = true
|
||||
|
||||
thread
|
||||
.tag = .thread
|
||||
.declspec = true
|
||||
|
||||
uuid
|
||||
.tag = .uuid
|
||||
.declspec = true
|
||||
|
||||
17162
deps/aro/aro/Builtins/Builtin.def
vendored
17162
deps/aro/aro/Builtins/Builtin.def
vendored
File diff suppressed because it is too large
Load diff
2446
deps/aro/aro/Diagnostics/messages.def
vendored
2446
deps/aro/aro/Diagnostics/messages.def
vendored
File diff suppressed because it is too large
Load diff
683
deps/aro/build/GenerateDef.zig
vendored
683
deps/aro/build/GenerateDef.zig
vendored
|
|
@ -1,683 +0,0 @@
|
|||
const std = @import("std");
|
||||
const Step = std.Build.Step;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const GeneratedFile = std.Build.GeneratedFile;
|
||||
|
||||
const GenerateDef = @This();
|
||||
|
||||
step: Step,
|
||||
path: []const u8,
|
||||
name: []const u8,
|
||||
kind: Options.Kind,
|
||||
generated_file: GeneratedFile,
|
||||
|
||||
pub const base_id: Step.Id = .custom;
|
||||
|
||||
pub const Options = struct {
|
||||
name: []const u8,
|
||||
src_prefix: []const u8 = "src/aro",
|
||||
kind: Kind = .dafsa,
|
||||
|
||||
pub const Kind = enum { dafsa, named };
|
||||
};
|
||||
|
||||
pub fn create(owner: *std.Build, options: Options) std.Build.Module.Import {
|
||||
const self = owner.allocator.create(GenerateDef) catch @panic("OOM");
|
||||
const path = owner.pathJoin(&.{ options.src_prefix, options.name });
|
||||
|
||||
const name = owner.fmt("GenerateDef {s}", .{options.name});
|
||||
self.* = .{
|
||||
.step = Step.init(.{
|
||||
.id = base_id,
|
||||
.name = name,
|
||||
.owner = owner,
|
||||
.makeFn = make,
|
||||
}),
|
||||
.path = path,
|
||||
.name = options.name,
|
||||
.kind = options.kind,
|
||||
.generated_file = .{ .step = &self.step },
|
||||
};
|
||||
const module = self.step.owner.createModule(.{
|
||||
.root_source_file = .{ .generated = &self.generated_file },
|
||||
});
|
||||
return .{
|
||||
.module = module,
|
||||
.name = self.name,
|
||||
};
|
||||
}
|
||||
|
||||
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
||||
_ = prog_node;
|
||||
const b = step.owner;
|
||||
const self = @fieldParentPtr(GenerateDef, "step", step);
|
||||
const arena = b.allocator;
|
||||
|
||||
var man = b.graph.cache.obtain();
|
||||
defer man.deinit();
|
||||
|
||||
// Random bytes to make GenerateDef unique. Refresh this with new
|
||||
// random bytes when GenerateDef implementation is modified in a
|
||||
// non-backwards-compatible way.
|
||||
man.hash.add(@as(u32, 0xDCC14144));
|
||||
|
||||
const contents = try b.build_root.handle.readFileAlloc(arena, self.path, std.math.maxInt(u32));
|
||||
man.hash.addBytes(contents);
|
||||
|
||||
const out_name = b.fmt("{s}.zig", .{std.fs.path.stem(self.path)});
|
||||
if (try step.cacheHit(&man)) {
|
||||
const digest = man.final();
|
||||
self.generated_file.path = try b.cache_root.join(arena, &.{
|
||||
"o", &digest, out_name,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const digest = man.final();
|
||||
|
||||
const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, out_name });
|
||||
const sub_path_dirname = std.fs.path.dirname(sub_path).?;
|
||||
|
||||
b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
|
||||
return step.fail("unable to make path '{}{s}': {s}", .{
|
||||
b.cache_root, sub_path_dirname, @errorName(err),
|
||||
});
|
||||
};
|
||||
|
||||
const output = try self.generate(contents);
|
||||
b.cache_root.handle.writeFile(sub_path, output) catch |err| {
|
||||
return step.fail("unable to write file '{}{s}': {s}", .{
|
||||
b.cache_root, sub_path, @errorName(err),
|
||||
});
|
||||
};
|
||||
|
||||
self.generated_file.path = try b.cache_root.join(arena, &.{sub_path});
|
||||
try man.writeManifest();
|
||||
}
|
||||
|
||||
const Value = struct {
|
||||
name: []const u8,
|
||||
properties: []const []const u8,
|
||||
};
|
||||
|
||||
fn generate(self: *GenerateDef, input: []const u8) ![]const u8 {
|
||||
const arena = self.step.owner.allocator;
|
||||
|
||||
var values = std.StringArrayHashMap([]const []const u8).init(arena);
|
||||
defer values.deinit();
|
||||
var properties = std.ArrayList([]const u8).init(arena);
|
||||
defer properties.deinit();
|
||||
var headers = std.ArrayList([]const u8).init(arena);
|
||||
defer headers.deinit();
|
||||
|
||||
var value_name: ?[]const u8 = null;
|
||||
var it = std.mem.tokenizeAny(u8, input, "\r\n");
|
||||
while (it.next()) |line_untrimmed| {
|
||||
const line = std.mem.trim(u8, line_untrimmed, " \t");
|
||||
if (line.len == 0 or line[0] == '#') continue;
|
||||
if (std.mem.startsWith(u8, line, "const ") or std.mem.startsWith(u8, line, "pub const ")) {
|
||||
try headers.append(line);
|
||||
continue;
|
||||
}
|
||||
if (line[0] == '.') {
|
||||
if (value_name == null) {
|
||||
return self.step.fail("property not attached to a value:\n\"{s}\"", .{line});
|
||||
}
|
||||
try properties.append(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value_name) |name| {
|
||||
const old = try values.fetchPut(name, try properties.toOwnedSlice());
|
||||
if (old != null) return self.step.fail("duplicate value \"{s}\"", .{name});
|
||||
}
|
||||
value_name = line;
|
||||
}
|
||||
|
||||
if (value_name) |name| {
|
||||
const old = try values.fetchPut(name, try properties.toOwnedSlice());
|
||||
if (old != null) return self.step.fail("duplicate value \"{s}\"", .{name});
|
||||
}
|
||||
|
||||
{
|
||||
const sorted_list = try arena.dupe([]const u8, values.keys());
|
||||
defer arena.free(sorted_list);
|
||||
std.mem.sort([]const u8, sorted_list, {}, struct {
|
||||
pub fn lessThan(_: void, a: []const u8, b: []const u8) bool {
|
||||
return std.mem.lessThan(u8, a, b);
|
||||
}
|
||||
}.lessThan);
|
||||
|
||||
var longest_name: usize = 0;
|
||||
var shortest_name: usize = std.math.maxInt(usize);
|
||||
|
||||
var builder = try DafsaBuilder.init(arena);
|
||||
defer builder.deinit();
|
||||
for (sorted_list) |name| {
|
||||
try builder.insert(name);
|
||||
longest_name = @max(name.len, longest_name);
|
||||
shortest_name = @min(name.len, shortest_name);
|
||||
}
|
||||
try builder.finish();
|
||||
builder.calcNumbers();
|
||||
|
||||
// As a sanity check, confirm that the minimal perfect hashing doesn't
|
||||
// have any collisions
|
||||
{
|
||||
var index_set = std.AutoHashMap(usize, void).init(arena);
|
||||
defer index_set.deinit();
|
||||
|
||||
for (values.keys()) |name| {
|
||||
const index = builder.getUniqueIndex(name).?;
|
||||
const result = try index_set.getOrPut(index);
|
||||
if (result.found_existing) {
|
||||
return self.step.fail("clobbered {}, name={s}\n", .{ index, name });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var out_buf = std.ArrayList(u8).init(arena);
|
||||
defer out_buf.deinit();
|
||||
const writer = out_buf.writer();
|
||||
|
||||
try writer.print(
|
||||
\\//! Autogenerated by GenerateDef from {s}, do not edit
|
||||
\\
|
||||
\\const std = @import("std");
|
||||
\\
|
||||
\\pub fn with(comptime Properties: type) type {{
|
||||
\\return struct {{
|
||||
\\
|
||||
, .{self.path});
|
||||
for (headers.items) |line| {
|
||||
try writer.print("{s}\n", .{line});
|
||||
}
|
||||
if (self.kind == .named) {
|
||||
try writer.writeAll("pub const Tag = enum {\n");
|
||||
for (values.keys()) |property| {
|
||||
try writer.print(" {s},\n", .{std.zig.fmtId(property)});
|
||||
}
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\ pub fn property(tag: Tag) Properties {
|
||||
\\ return named_data[@intFromEnum(tag)];
|
||||
\\ }
|
||||
\\
|
||||
\\ const named_data = [_]Properties{
|
||||
\\
|
||||
);
|
||||
for (values.values()) |val_props| {
|
||||
try writer.writeAll(" .{");
|
||||
for (val_props, 0..) |val_prop, j| {
|
||||
if (j != 0) try writer.writeByte(',');
|
||||
try writer.writeByte(' ');
|
||||
try writer.writeAll(val_prop);
|
||||
}
|
||||
try writer.writeAll(" },\n");
|
||||
}
|
||||
try writer.writeAll(
|
||||
\\ };
|
||||
\\};
|
||||
\\};
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
|
||||
return out_buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
var values_array = try arena.alloc(Value, values.count());
|
||||
defer arena.free(values_array);
|
||||
|
||||
for (values.keys(), values.values()) |name, props| {
|
||||
const unique_index = builder.getUniqueIndex(name).?;
|
||||
const data_index = unique_index - 1;
|
||||
values_array[data_index] = .{ .name = name, .properties = props };
|
||||
}
|
||||
|
||||
try writer.writeAll(
|
||||
\\
|
||||
\\tag: Tag,
|
||||
\\properties: Properties,
|
||||
\\
|
||||
\\/// Integer starting at 0 derived from the unique index,
|
||||
\\/// corresponds with the data array index.
|
||||
\\pub const Tag = enum(u16) { _ };
|
||||
\\
|
||||
\\const Self = @This();
|
||||
\\
|
||||
\\pub fn fromName(name: []const u8) ?@This() {
|
||||
\\ const data_index = tagFromName(name) orelse return null;
|
||||
\\ return data[@intFromEnum(data_index)];
|
||||
\\}
|
||||
\\
|
||||
\\pub fn tagFromName(name: []const u8) ?Tag {
|
||||
\\ const unique_index = uniqueIndex(name) orelse return null;
|
||||
\\ return @enumFromInt(unique_index - 1);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn fromTag(tag: Tag) @This() {
|
||||
\\ return data[@intFromEnum(tag)];
|
||||
\\}
|
||||
\\
|
||||
\\pub fn nameFromTagIntoBuf(tag: Tag, name_buf: []u8) []u8 {
|
||||
\\ std.debug.assert(name_buf.len >= longest_name);
|
||||
\\ const unique_index = @intFromEnum(tag) + 1;
|
||||
\\ return nameFromUniqueIndex(unique_index, name_buf);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn nameFromTag(tag: Tag) NameBuf {
|
||||
\\ var name_buf: NameBuf = undefined;
|
||||
\\ const unique_index = @intFromEnum(tag) + 1;
|
||||
\\ const name = nameFromUniqueIndex(unique_index, &name_buf.buf);
|
||||
\\ name_buf.len = @intCast(name.len);
|
||||
\\ return name_buf;
|
||||
\\}
|
||||
\\
|
||||
\\pub const NameBuf = struct {
|
||||
\\ buf: [longest_name]u8 = undefined,
|
||||
\\ len: std.math.IntFittingRange(0, longest_name),
|
||||
\\
|
||||
\\ pub fn span(self: *const NameBuf) []const u8 {
|
||||
\\ return self.buf[0..self.len];
|
||||
\\ }
|
||||
\\};
|
||||
\\
|
||||
\\pub fn exists(name: []const u8) bool {
|
||||
\\ if (name.len < shortest_name or name.len > longest_name) return false;
|
||||
\\
|
||||
\\ var index: u16 = 0;
|
||||
\\ for (name) |c| {
|
||||
\\ index = findInList(dafsa[index].child_index, c) orelse return false;
|
||||
\\ }
|
||||
\\ return dafsa[index].end_of_word;
|
||||
\\}
|
||||
\\
|
||||
\\
|
||||
);
|
||||
try writer.print("pub const shortest_name = {};\n", .{shortest_name});
|
||||
try writer.print("pub const longest_name = {};\n\n", .{longest_name});
|
||||
try writer.writeAll(
|
||||
\\/// Search siblings of `first_child_index` for the `char`
|
||||
\\/// If found, returns the index of the node within the `dafsa` array.
|
||||
\\/// Otherwise, returns `null`.
|
||||
\\pub fn findInList(first_child_index: u16, char: u8) ?u16 {
|
||||
\\ var index = first_child_index;
|
||||
\\ while (true) {
|
||||
\\ if (dafsa[index].char == char) return index;
|
||||
\\ if (dafsa[index].end_of_list) return null;
|
||||
\\ index += 1;
|
||||
\\ }
|
||||
\\ unreachable;
|
||||
\\}
|
||||
\\
|
||||
\\/// Returns a unique (minimal perfect hash) index (starting at 1) for the `name`,
|
||||
\\/// or null if the name was not found.
|
||||
\\pub fn uniqueIndex(name: []const u8) ?u16 {
|
||||
\\ if (name.len < shortest_name or name.len > longest_name) return null;
|
||||
\\
|
||||
\\ var index: u16 = 0;
|
||||
\\ var node_index: u16 = 0;
|
||||
\\
|
||||
\\ for (name) |c| {
|
||||
\\ const child_index = findInList(dafsa[node_index].child_index, c) orelse return null;
|
||||
\\ var sibling_index = dafsa[node_index].child_index;
|
||||
\\ while (true) {
|
||||
\\ const sibling_c = dafsa[sibling_index].char;
|
||||
\\ std.debug.assert(sibling_c != 0);
|
||||
\\ if (sibling_c < c) {
|
||||
\\ index += dafsa[sibling_index].number;
|
||||
\\ }
|
||||
\\ if (dafsa[sibling_index].end_of_list) break;
|
||||
\\ sibling_index += 1;
|
||||
\\ }
|
||||
\\ node_index = child_index;
|
||||
\\ if (dafsa[node_index].end_of_word) index += 1;
|
||||
\\ }
|
||||
\\
|
||||
\\ if (!dafsa[node_index].end_of_word) return null;
|
||||
\\
|
||||
\\ return index;
|
||||
\\}
|
||||
\\
|
||||
\\/// Returns a slice of `buf` with the name associated with the given `index`.
|
||||
\\/// This function should only be called with an `index` that
|
||||
\\/// is already known to exist within the `dafsa`, e.g. an index
|
||||
\\/// returned from `uniqueIndex`.
|
||||
\\pub fn nameFromUniqueIndex(index: u16, buf: []u8) []u8 {
|
||||
\\ std.debug.assert(index >= 1 and index <= data.len);
|
||||
\\
|
||||
\\ var node_index: u16 = 0;
|
||||
\\ var count: u16 = index;
|
||||
\\ var fbs = std.io.fixedBufferStream(buf);
|
||||
\\ const w = fbs.writer();
|
||||
\\
|
||||
\\ while (true) {
|
||||
\\ var sibling_index = dafsa[node_index].child_index;
|
||||
\\ while (true) {
|
||||
\\ if (dafsa[sibling_index].number > 0 and dafsa[sibling_index].number < count) {
|
||||
\\ count -= dafsa[sibling_index].number;
|
||||
\\ } else {
|
||||
\\ w.writeByte(dafsa[sibling_index].char) catch unreachable;
|
||||
\\ node_index = sibling_index;
|
||||
\\ if (dafsa[node_index].end_of_word) {
|
||||
\\ count -= 1;
|
||||
\\ }
|
||||
\\ break;
|
||||
\\ }
|
||||
\\
|
||||
\\ if (dafsa[sibling_index].end_of_list) break;
|
||||
\\ sibling_index += 1;
|
||||
\\ }
|
||||
\\ if (count == 0) break;
|
||||
\\ }
|
||||
\\
|
||||
\\ return fbs.getWritten();
|
||||
\\}
|
||||
\\
|
||||
\\
|
||||
);
|
||||
try writer.writeAll(
|
||||
\\/// We're 1 bit shy of being able to fit this in a u32:
|
||||
\\/// - char only contains 0-9, a-z, A-Z, and _, so it could use a enum(u6) with a way to convert <-> u8
|
||||
\\/// (note: this would have a performance cost that may make the u32 not worth it)
|
||||
\\/// - number has a max value of > 2047 and < 4095 (the first _ node has the largest number),
|
||||
\\/// so it could fit into a u12
|
||||
\\/// - child_index currently has a max of > 4095 and < 8191, so it could fit into a u13
|
||||
\\///
|
||||
\\/// with the end_of_word/end_of_list 2 bools, that makes 33 bits total
|
||||
\\const Node = packed struct(u64) {
|
||||
\\ char: u8,
|
||||
\\ /// Nodes are numbered with "an integer which gives the number of words that
|
||||
\\ /// would be accepted by the automaton starting from that state." This numbering
|
||||
\\ /// allows calculating "a one-to-one correspondence between the integers 1 to L
|
||||
\\ /// (L is the number of words accepted by the automaton) and the words themselves."
|
||||
\\ ///
|
||||
\\ /// Essentially, this allows us to have a minimal perfect hashing scheme such that
|
||||
\\ /// it's possible to store & lookup the properties of each builtin using a separate array.
|
||||
\\ number: u16,
|
||||
\\ /// If true, this node is the end of a valid builtin.
|
||||
\\ /// Note: This does not necessarily mean that this node does not have child nodes.
|
||||
\\ end_of_word: bool,
|
||||
\\ /// If true, this node is the end of a sibling list.
|
||||
\\ /// If false, then (index + 1) will contain the next sibling.
|
||||
\\ end_of_list: bool,
|
||||
\\ /// Padding bits to get to u64, unsure if there's some way to use these to improve something.
|
||||
\\ _extra: u22 = 0,
|
||||
\\ /// Index of the first child of this node.
|
||||
\\ child_index: u16,
|
||||
\\};
|
||||
\\
|
||||
\\
|
||||
);
|
||||
try builder.writeDafsa(writer);
|
||||
try writeData(writer, values_array);
|
||||
try writer.writeAll(
|
||||
\\};
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
|
||||
return out_buf.toOwnedSlice();
|
||||
}
|
||||
}
|
||||
|
||||
fn writeData(writer: anytype, values: []const Value) !void {
|
||||
try writer.writeAll("pub const data = blk: {\n");
|
||||
try writer.print(" @setEvalBranchQuota({});\n", .{values.len});
|
||||
try writer.writeAll(" break :blk [_]@This(){\n");
|
||||
for (values, 0..) |value, i| {
|
||||
try writer.print(" // {s}\n", .{value.name});
|
||||
try writer.print(" .{{ .tag = @enumFromInt({}), .properties = .{{", .{i});
|
||||
for (value.properties, 0..) |property, j| {
|
||||
if (j != 0) try writer.writeByte(',');
|
||||
try writer.writeByte(' ');
|
||||
try writer.writeAll(property);
|
||||
}
|
||||
if (value.properties.len != 0) try writer.writeByte(' ');
|
||||
try writer.writeAll("} },\n");
|
||||
}
|
||||
try writer.writeAll(" };\n");
|
||||
try writer.writeAll("};\n");
|
||||
}
|
||||
|
||||
const DafsaBuilder = struct {
|
||||
root: *Node,
|
||||
arena: std.heap.ArenaAllocator.State,
|
||||
allocator: Allocator,
|
||||
unchecked_nodes: std.ArrayListUnmanaged(UncheckedNode),
|
||||
minimized_nodes: std.HashMapUnmanaged(*Node, *Node, Node.DuplicateContext, std.hash_map.default_max_load_percentage),
|
||||
previous_word_buf: [128]u8 = undefined,
|
||||
previous_word: []u8 = &[_]u8{},
|
||||
|
||||
const UncheckedNode = struct {
|
||||
parent: *Node,
|
||||
char: u8,
|
||||
child: *Node,
|
||||
};
|
||||
|
||||
pub fn init(allocator: Allocator) !DafsaBuilder {
|
||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||
errdefer arena.deinit();
|
||||
|
||||
const root = try arena.allocator().create(Node);
|
||||
root.* = .{};
|
||||
return DafsaBuilder{
|
||||
.root = root,
|
||||
.allocator = allocator,
|
||||
.arena = arena.state,
|
||||
.unchecked_nodes = .{},
|
||||
.minimized_nodes = .{},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DafsaBuilder) void {
|
||||
self.arena.promote(self.allocator).deinit();
|
||||
self.unchecked_nodes.deinit(self.allocator);
|
||||
self.minimized_nodes.deinit(self.allocator);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
const Node = struct {
|
||||
children: [256]?*Node = [_]?*Node{null} ** 256,
|
||||
is_terminal: bool = false,
|
||||
number: usize = 0,
|
||||
|
||||
const DuplicateContext = struct {
|
||||
pub fn hash(ctx: @This(), key: *Node) u64 {
|
||||
_ = ctx;
|
||||
var hasher = std.hash.Wyhash.init(0);
|
||||
std.hash.autoHash(&hasher, key.children);
|
||||
std.hash.autoHash(&hasher, key.is_terminal);
|
||||
return hasher.final();
|
||||
}
|
||||
|
||||
pub fn eql(ctx: @This(), a: *Node, b: *Node) bool {
|
||||
_ = ctx;
|
||||
return a.is_terminal == b.is_terminal and std.mem.eql(?*Node, &a.children, &b.children);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn calcNumbers(self: *Node) void {
|
||||
self.number = @intFromBool(self.is_terminal);
|
||||
for (self.children) |maybe_child| {
|
||||
const child = maybe_child orelse continue;
|
||||
// A node's number is the sum of the
|
||||
// numbers of its immediate child nodes.
|
||||
child.calcNumbers();
|
||||
self.number += child.number;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn numDirectChildren(self: *const Node) u8 {
|
||||
var num: u8 = 0;
|
||||
for (self.children) |child| {
|
||||
if (child != null) num += 1;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn insert(self: *DafsaBuilder, str: []const u8) !void {
|
||||
if (std.mem.order(u8, str, self.previous_word) == .lt) {
|
||||
@panic("insertion order must be sorted");
|
||||
}
|
||||
|
||||
var common_prefix_len: usize = 0;
|
||||
for (0..@min(str.len, self.previous_word.len)) |i| {
|
||||
if (str[i] != self.previous_word[i]) break;
|
||||
common_prefix_len += 1;
|
||||
}
|
||||
|
||||
try self.minimize(common_prefix_len);
|
||||
|
||||
var node = if (self.unchecked_nodes.items.len == 0)
|
||||
self.root
|
||||
else
|
||||
self.unchecked_nodes.getLast().child;
|
||||
|
||||
for (str[common_prefix_len..]) |c| {
|
||||
std.debug.assert(node.children[c] == null);
|
||||
|
||||
var arena = self.arena.promote(self.allocator);
|
||||
const child = try arena.allocator().create(Node);
|
||||
self.arena = arena.state;
|
||||
|
||||
child.* = .{};
|
||||
node.children[c] = child;
|
||||
try self.unchecked_nodes.append(self.allocator, .{
|
||||
.parent = node,
|
||||
.char = c,
|
||||
.child = child,
|
||||
});
|
||||
node = node.children[c].?;
|
||||
}
|
||||
node.is_terminal = true;
|
||||
|
||||
self.previous_word = self.previous_word_buf[0..str.len];
|
||||
@memcpy(self.previous_word, str);
|
||||
}
|
||||
|
||||
pub fn minimize(self: *DafsaBuilder, down_to: usize) !void {
|
||||
if (self.unchecked_nodes.items.len == 0) return;
|
||||
while (self.unchecked_nodes.items.len > down_to) {
|
||||
const unchecked_node = self.unchecked_nodes.pop();
|
||||
if (self.minimized_nodes.getPtr(unchecked_node.child)) |child| {
|
||||
unchecked_node.parent.children[unchecked_node.char] = child.*;
|
||||
} else {
|
||||
try self.minimized_nodes.put(self.allocator, unchecked_node.child, unchecked_node.child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self: *DafsaBuilder) !void {
|
||||
try self.minimize(0);
|
||||
}
|
||||
|
||||
fn nodeCount(self: *const DafsaBuilder) usize {
|
||||
return self.minimized_nodes.count();
|
||||
}
|
||||
|
||||
fn edgeCount(self: *const DafsaBuilder) usize {
|
||||
var count: usize = 0;
|
||||
var it = self.minimized_nodes.iterator();
|
||||
while (it.next()) |entry| {
|
||||
for (entry.key_ptr.*.children) |child| {
|
||||
if (child != null) count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
fn contains(self: *const DafsaBuilder, str: []const u8) bool {
|
||||
var node = self.root;
|
||||
for (str) |c| {
|
||||
node = node.children[c] orelse return false;
|
||||
}
|
||||
return node.is_terminal;
|
||||
}
|
||||
|
||||
fn calcNumbers(self: *const DafsaBuilder) void {
|
||||
self.root.calcNumbers();
|
||||
}
|
||||
|
||||
fn getUniqueIndex(self: *const DafsaBuilder, str: []const u8) ?usize {
|
||||
var index: usize = 0;
|
||||
var node = self.root;
|
||||
|
||||
for (str) |c| {
|
||||
const child = node.children[c] orelse return null;
|
||||
for (node.children, 0..) |sibling, sibling_c| {
|
||||
if (sibling == null) continue;
|
||||
if (sibling_c < c) {
|
||||
index += sibling.?.number;
|
||||
}
|
||||
}
|
||||
node = child;
|
||||
if (node.is_terminal) index += 1;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
fn writeDafsa(self: *const DafsaBuilder, writer: anytype) !void {
|
||||
try writer.writeAll("const dafsa = [_]Node{\n");
|
||||
|
||||
// write root
|
||||
try writer.writeAll(" .{ .char = 0, .end_of_word = false, .end_of_list = true, .number = 0, .child_index = 1 },\n");
|
||||
|
||||
var queue = std.ArrayList(*Node).init(self.allocator);
|
||||
defer queue.deinit();
|
||||
|
||||
var child_indexes = std.AutoHashMap(*Node, usize).init(self.allocator);
|
||||
defer child_indexes.deinit();
|
||||
|
||||
try child_indexes.ensureTotalCapacity(@intCast(self.edgeCount()));
|
||||
|
||||
var first_available_index: usize = self.root.numDirectChildren() + 1;
|
||||
first_available_index = try writeDafsaChildren(self.root, writer, &queue, &child_indexes, first_available_index);
|
||||
|
||||
while (queue.items.len > 0) {
|
||||
// TODO: something with better time complexity
|
||||
const node = queue.orderedRemove(0);
|
||||
|
||||
first_available_index = try writeDafsaChildren(node, writer, &queue, &child_indexes, first_available_index);
|
||||
}
|
||||
|
||||
try writer.writeAll("};\n");
|
||||
}
|
||||
|
||||
fn writeDafsaChildren(
|
||||
node: *Node,
|
||||
writer: anytype,
|
||||
queue: *std.ArrayList(*Node),
|
||||
child_indexes: *std.AutoHashMap(*Node, usize),
|
||||
first_available_index: usize,
|
||||
) !usize {
|
||||
var cur_available_index = first_available_index;
|
||||
const num_children = node.numDirectChildren();
|
||||
var child_i: usize = 0;
|
||||
for (node.children, 0..) |maybe_child, c_usize| {
|
||||
const child = maybe_child orelse continue;
|
||||
const c: u8 = @intCast(c_usize);
|
||||
const is_last_child = child_i == num_children - 1;
|
||||
|
||||
if (!child_indexes.contains(child)) {
|
||||
const child_num_children = child.numDirectChildren();
|
||||
if (child_num_children > 0) {
|
||||
child_indexes.putAssumeCapacityNoClobber(child, cur_available_index);
|
||||
cur_available_index += child_num_children;
|
||||
}
|
||||
try queue.append(child);
|
||||
}
|
||||
|
||||
try writer.print(
|
||||
" .{{ .char = '{c}', .end_of_word = {}, .end_of_list = {}, .number = {}, .child_index = {} }},\n",
|
||||
.{ c, child.is_terminal, is_last_child, child.number, child_indexes.get(child) orelse 0 },
|
||||
);
|
||||
|
||||
child_i += 1;
|
||||
}
|
||||
return cur_available_index;
|
||||
}
|
||||
};
|
||||
|
|
@ -13,7 +13,7 @@ pub const TypeMapper = @import("aro/StringInterner.zig").TypeMapper;
|
|||
pub const target_util = @import("aro/target.zig");
|
||||
pub const Value = @import("aro/Value.zig");
|
||||
|
||||
const backend = @import("backend");
|
||||
const backend = @import("backend.zig");
|
||||
pub const Interner = backend.Interner;
|
||||
pub const Ir = backend.Ir;
|
||||
pub const Object = backend.Object;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const ZigType = std.builtin.Type;
|
||||
const CallingConvention = @import("backend").CallingConvention;
|
||||
const CallingConvention = @import("../backend.zig").CallingConvention;
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const Diagnostics = @import("Diagnostics.zig");
|
||||
const Parser = @import("Parser.zig");
|
||||
|
|
@ -687,7 +687,7 @@ pub fn fromString(kind: Kind, namespace: ?[]const u8, name: []const u8) ?Tag {
|
|||
declspec: bool = false,
|
||||
c23: bool = false,
|
||||
};
|
||||
const attribute_names = @import("Attribute/names.def").with(Properties);
|
||||
const attribute_names = @import("Attribute/names.zig").with(Properties);
|
||||
|
||||
const normalized = normalize(name);
|
||||
const actual_kind: Kind = if (namespace) |ns| blk: {
|
||||
1011
lib/compiler/aro/aro/Attribute/names.zig
Normal file
1011
lib/compiler/aro/aro/Attribute/names.zig
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,7 @@ const LangOpts = @import("LangOpts.zig");
|
|||
const Parser = @import("Parser.zig");
|
||||
|
||||
const Properties = @import("Builtins/Properties.zig");
|
||||
pub const Builtin = @import("Builtins/Builtin.def").with(Properties);
|
||||
pub const Builtin = @import("Builtins/Builtin.zig").with(Properties);
|
||||
|
||||
const Expanded = struct {
|
||||
ty: Type,
|
||||
13145
lib/compiler/aro/aro/Builtins/Builtin.zig
Normal file
13145
lib/compiler/aro/aro/Builtins/Builtin.zig
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const backend = @import("backend");
|
||||
const backend = @import("../backend.zig");
|
||||
const Interner = backend.Interner;
|
||||
const Ir = backend.Ir;
|
||||
const Builtins = @import("Builtins.zig");
|
||||
|
|
@ -3,7 +3,7 @@ const Allocator = mem.Allocator;
|
|||
const assert = std.debug.assert;
|
||||
const EpochSeconds = std.time.epoch.EpochSeconds;
|
||||
const mem = std.mem;
|
||||
const Interner = @import("backend").Interner;
|
||||
const Interner = @import("../backend.zig").Interner;
|
||||
const Builtins = @import("Builtins.zig");
|
||||
const Builtin = Builtins.Builtin;
|
||||
const Diagnostics = @import("Diagnostics.zig");
|
||||
|
|
@ -507,7 +507,7 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi
|
|||
if (system_defines_mode == .include_system_defines) {
|
||||
try buf.appendSlice(
|
||||
\\#define __VERSION__ "Aro
|
||||
++ @import("backend").version_str ++ "\"\n" ++
|
||||
++ @import("../backend.zig").version_str ++ "\"\n" ++
|
||||
\\#define __Aro__
|
||||
\\
|
||||
);
|
||||
|
|
@ -92,7 +92,7 @@ const Properties = struct {
|
|||
pub const max_bits = Compilation.bit_int_max_bits;
|
||||
};
|
||||
|
||||
pub const Tag = @import("Diagnostics/messages.def").with(Properties).Tag;
|
||||
pub const Tag = @import("Diagnostics/messages.zig").with(Properties).Tag;
|
||||
|
||||
pub const Kind = enum { @"fatal error", @"error", note, warning, off, default };
|
||||
|
||||
1011
lib/compiler/aro/aro/Diagnostics/messages.zig
Normal file
1011
lib/compiler/aro/aro/Diagnostics/messages.zig
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const process = std.process;
|
||||
const backend = @import("backend");
|
||||
const backend = @import("../backend.zig");
|
||||
const Ir = backend.Ir;
|
||||
const Object = backend.Object;
|
||||
const Compilation = @import("Compilation.zig");
|
||||
|
|
@ -189,7 +189,7 @@ pub fn parseArgs(
|
|||
};
|
||||
return true;
|
||||
} else if (mem.eql(u8, arg, "-v") or mem.eql(u8, arg, "--version")) {
|
||||
std_out.writeAll(@import("backend").version_str ++ "\n") catch |er| {
|
||||
std_out.writeAll(@import("../backend.zig").version_str ++ "\n") catch |er| {
|
||||
return d.fatal("unable to print version: {s}", .{errorDescription(er)});
|
||||
};
|
||||
return true;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const Interner = @import("backend").Interner;
|
||||
const Interner = @import("../backend.zig").Interner;
|
||||
const Attribute = @import("Attribute.zig");
|
||||
const CodeGen = @import("CodeGen.zig");
|
||||
const Compilation = @import("Compilation.zig");
|
||||
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
|||
const assert = std.debug.assert;
|
||||
const BigIntConst = std.math.big.int.Const;
|
||||
const BigIntMutable = std.math.big.int.Mutable;
|
||||
const backend = @import("backend");
|
||||
const backend = @import("../backend.zig");
|
||||
const Interner = backend.Interner;
|
||||
const BigIntSpace = Interner.Tag.Int.BigIntSpace;
|
||||
const Compilation = @import("Compilation.zig");
|
||||
|
|
@ -9,5 +9,5 @@ pub const CallingConvention = enum {
|
|||
vectorcall,
|
||||
};
|
||||
|
||||
pub const version_str = @import("build_options").version_str;
|
||||
pub const version_str = "aro-zig";
|
||||
pub const version = @import("std").SemanticVersion.parse(version_str) catch unreachable;
|
||||
1298
lib/compiler/aro_translate_c.zig
Normal file
1298
lib/compiler/aro_translate_c.zig
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,4 @@
|
|||
const std = @import("std");
|
||||
const Type = @import("../type.zig").Type;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const Node = extern union {
|
||||
|
|
@ -4007,8 +4007,6 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
|
|||
}
|
||||
var tree = switch (comp.config.c_frontend) {
|
||||
.aro => tree: {
|
||||
const translate_c = @import("aro_translate_c.zig");
|
||||
_ = translate_c;
|
||||
if (true) @panic("TODO");
|
||||
break :tree undefined;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,678 +0,0 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const assert = std.debug.assert;
|
||||
const CallingConvention = std.builtin.CallingConvention;
|
||||
const translate_c = @import("translate_c.zig");
|
||||
const aro = @import("aro");
|
||||
const Tree = aro.Tree;
|
||||
const NodeIndex = Tree.NodeIndex;
|
||||
const TokenIndex = Tree.TokenIndex;
|
||||
const Type = aro.Type;
|
||||
const ast = @import("translate_c/ast.zig");
|
||||
const ZigNode = ast.Node;
|
||||
const ZigTag = ZigNode.Tag;
|
||||
const common = @import("translate_c/common.zig");
|
||||
const Error = common.Error;
|
||||
const MacroProcessingError = common.MacroProcessingError;
|
||||
const TypeError = common.TypeError;
|
||||
const TransError = common.TransError;
|
||||
const SymbolTable = common.SymbolTable;
|
||||
const AliasList = common.AliasList;
|
||||
const ResultUsed = common.ResultUsed;
|
||||
const Scope = common.ScopeExtra(Context, Type);
|
||||
|
||||
const Context = struct {
|
||||
gpa: mem.Allocator,
|
||||
arena: mem.Allocator,
|
||||
decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
|
||||
alias_list: AliasList,
|
||||
global_scope: *Scope.Root,
|
||||
mangle_count: u32 = 0,
|
||||
/// Table of record decls that have been demoted to opaques.
|
||||
opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
|
||||
/// Table of unnamed enums and records that are child types of typedefs.
|
||||
unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{},
|
||||
/// Needed to decide if we are parsing a typename
|
||||
typedefs: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
|
||||
/// This one is different than the root scope's name table. This contains
|
||||
/// a list of names that we found by visiting all the top level decls without
|
||||
/// translating them. The other maps are updated as we translate; this one is updated
|
||||
/// up front in a pre-processing step.
|
||||
global_names: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
|
||||
/// This is similar to `global_names`, but contains names which we would
|
||||
/// *like* to use, but do not strictly *have* to if they are unavailable.
|
||||
/// These are relevant to types, which ideally we would name like
|
||||
/// 'struct_foo' with an alias 'foo', but if either of those names is taken,
|
||||
/// may be mangled.
|
||||
/// This is distinct from `global_names` so we can detect at a type
|
||||
/// declaration whether or not the name is available.
|
||||
weak_global_names: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
|
||||
pattern_list: translate_c.PatternList,
|
||||
tree: Tree,
|
||||
comp: *aro.Compilation,
|
||||
mapper: aro.TypeMapper,
|
||||
|
||||
fn getMangle(c: *Context) u32 {
|
||||
c.mangle_count += 1;
|
||||
return c.mangle_count;
|
||||
}
|
||||
|
||||
/// Convert a clang source location to a file:line:column string
|
||||
fn locStr(c: *Context, loc: TokenIndex) ![]const u8 {
|
||||
_ = c;
|
||||
_ = loc;
|
||||
// const spelling_loc = c.source_manager.getSpellingLoc(loc);
|
||||
// const filename_c = c.source_manager.getFilename(spelling_loc);
|
||||
// const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");
|
||||
|
||||
// const line = c.source_manager.getSpellingLineNumber(spelling_loc);
|
||||
// const column = c.source_manager.getSpellingColumnNumber(spelling_loc);
|
||||
// return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });
|
||||
return "somewhere";
|
||||
}
|
||||
};
|
||||
|
||||
fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode {
|
||||
if (used == .used) return result;
|
||||
return ZigTag.discard.create(c.arena, .{ .should_skip = false, .value = result });
|
||||
}
|
||||
|
||||
fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: ZigNode) !void {
|
||||
const gop = try c.global_scope.sym_table.getOrPut(name);
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = decl_node;
|
||||
try c.global_scope.nodes.append(decl_node);
|
||||
}
|
||||
}
|
||||
|
||||
fn failDecl(c: *Context, loc: TokenIndex, name: []const u8, comptime format: []const u8, args: anytype) Error!void {
|
||||
// location
|
||||
// pub const name = @compileError(msg);
|
||||
const fail_msg = try std.fmt.allocPrint(c.arena, format, args);
|
||||
try addTopLevelDecl(c, name, try ZigTag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg }));
|
||||
const str = try c.locStr(loc);
|
||||
const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{str});
|
||||
try c.global_scope.nodes.append(try ZigTag.warning.create(c.arena, location_comment));
|
||||
}
|
||||
|
||||
fn warn(c: *Context, scope: *Scope, loc: TokenIndex, comptime format: []const u8, args: anytype) !void {
|
||||
const str = try c.locStr(loc);
|
||||
const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, .{str} ++ args);
|
||||
try scope.appendNode(try ZigTag.warning.create(c.arena, value));
|
||||
}
|
||||
|
||||
pub fn translate(
|
||||
gpa: mem.Allocator,
|
||||
comp: *aro.Compilation,
|
||||
args: []const []const u8,
|
||||
) !std.zig.Ast {
|
||||
try comp.addDefaultPragmaHandlers();
|
||||
comp.langopts.setEmulatedCompiler(aro.target_util.systemCompiler(comp.target));
|
||||
|
||||
var driver: aro.Driver = .{ .comp = comp };
|
||||
defer driver.deinit();
|
||||
|
||||
var macro_buf = std.ArrayList(u8).init(gpa);
|
||||
defer macro_buf.deinit();
|
||||
|
||||
assert(!try driver.parseArgs(std.io.null_writer, macro_buf.writer(), args));
|
||||
assert(driver.inputs.items.len == 1);
|
||||
const source = driver.inputs.items[0];
|
||||
|
||||
const builtin_macros = try comp.generateBuiltinMacros(.include_system_defines);
|
||||
const user_macros = try comp.addSourceFromBuffer("<command line>", macro_buf.items);
|
||||
|
||||
var pp = try aro.Preprocessor.initDefault(comp);
|
||||
defer pp.deinit();
|
||||
|
||||
try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
|
||||
|
||||
var tree = try pp.parse();
|
||||
defer tree.deinit();
|
||||
|
||||
if (driver.comp.diagnostics.errors != 0) {
|
||||
return error.SemanticAnalyzeFail;
|
||||
}
|
||||
|
||||
const mapper = tree.comp.string_interner.getFastTypeMapper(tree.comp.gpa) catch tree.comp.string_interner.getSlowTypeMapper();
|
||||
defer mapper.deinit(tree.comp.gpa);
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
var context = Context{
|
||||
.gpa = gpa,
|
||||
.arena = arena,
|
||||
.alias_list = AliasList.init(gpa),
|
||||
.global_scope = try arena.create(Scope.Root),
|
||||
.pattern_list = try translate_c.PatternList.init(gpa),
|
||||
.comp = comp,
|
||||
.mapper = mapper,
|
||||
.tree = tree,
|
||||
};
|
||||
context.global_scope.* = Scope.Root.init(&context);
|
||||
defer {
|
||||
context.decl_table.deinit(gpa);
|
||||
context.alias_list.deinit();
|
||||
context.global_names.deinit(gpa);
|
||||
context.opaque_demotes.deinit(gpa);
|
||||
context.unnamed_typedefs.deinit(gpa);
|
||||
context.typedefs.deinit(gpa);
|
||||
context.global_scope.deinit();
|
||||
context.pattern_list.deinit(gpa);
|
||||
}
|
||||
|
||||
inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {
|
||||
const builtin_fn = try ZigTag.pub_var_simple.create(arena, .{
|
||||
.name = decl.name,
|
||||
.init = try ZigTag.import_c_builtin.create(arena, decl.name),
|
||||
});
|
||||
try addTopLevelDecl(&context, decl.name, builtin_fn);
|
||||
}
|
||||
|
||||
try prepopulateGlobalNameTable(&context);
|
||||
try transTopLevelDecls(&context);
|
||||
|
||||
for (context.alias_list.items) |alias| {
|
||||
if (!context.global_scope.sym_table.contains(alias.alias)) {
|
||||
const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
|
||||
try addTopLevelDecl(&context, alias.alias, node);
|
||||
}
|
||||
}
|
||||
|
||||
return ast.render(gpa, context.global_scope.nodes.items);
|
||||
}
|
||||
|
||||
fn prepopulateGlobalNameTable(c: *Context) !void {
|
||||
const node_tags = c.tree.nodes.items(.tag);
|
||||
const node_types = c.tree.nodes.items(.ty);
|
||||
const node_data = c.tree.nodes.items(.data);
|
||||
for (c.tree.root_decls) |node| {
|
||||
const data = node_data[@intFromEnum(node)];
|
||||
const decl_name = switch (node_tags[@intFromEnum(node)]) {
|
||||
.typedef => @panic("TODO"),
|
||||
|
||||
.static_assert,
|
||||
.struct_decl_two,
|
||||
.union_decl_two,
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
=> blk: {
|
||||
const ty = node_types[@intFromEnum(node)];
|
||||
const name_id = ty.data.record.name;
|
||||
break :blk c.mapper.lookup(name_id);
|
||||
},
|
||||
|
||||
.enum_decl_two,
|
||||
.enum_decl,
|
||||
=> blk: {
|
||||
const ty = node_types[@intFromEnum(node)];
|
||||
const name_id = ty.data.@"enum".name;
|
||||
break :blk c.mapper.lookup(name_id);
|
||||
},
|
||||
|
||||
.fn_proto,
|
||||
.static_fn_proto,
|
||||
.inline_fn_proto,
|
||||
.inline_static_fn_proto,
|
||||
.fn_def,
|
||||
.static_fn_def,
|
||||
.inline_fn_def,
|
||||
.inline_static_fn_def,
|
||||
.@"var",
|
||||
.static_var,
|
||||
.threadlocal_var,
|
||||
.threadlocal_static_var,
|
||||
.extern_var,
|
||||
.threadlocal_extern_var,
|
||||
=> c.tree.tokSlice(data.decl.name),
|
||||
else => unreachable,
|
||||
};
|
||||
try c.global_names.put(c.gpa, decl_name, {});
|
||||
}
|
||||
}
|
||||
|
||||
fn transTopLevelDecls(c: *Context) !void {
|
||||
const node_tags = c.tree.nodes.items(.tag);
|
||||
const node_data = c.tree.nodes.items(.data);
|
||||
for (c.tree.root_decls) |node| {
|
||||
const data = node_data[@intFromEnum(node)];
|
||||
switch (node_tags[@intFromEnum(node)]) {
|
||||
.typedef => {
|
||||
try transTypeDef(c, &c.global_scope.base, node);
|
||||
},
|
||||
|
||||
.static_assert,
|
||||
.struct_decl_two,
|
||||
.union_decl_two,
|
||||
.struct_decl,
|
||||
.union_decl,
|
||||
=> {
|
||||
try transRecordDecl(c, &c.global_scope.base, node);
|
||||
},
|
||||
|
||||
.enum_decl_two => {
|
||||
var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
|
||||
var field_count: u8 = 0;
|
||||
if (fields[0] != .none) field_count += 1;
|
||||
if (fields[1] != .none) field_count += 1;
|
||||
try transEnumDecl(c, &c.global_scope.base, node, fields[0..field_count]);
|
||||
},
|
||||
.enum_decl => {
|
||||
const fields = c.tree.data[data.range.start..data.range.end];
|
||||
try transEnumDecl(c, &c.global_scope.base, node, fields);
|
||||
},
|
||||
|
||||
.fn_proto,
|
||||
.static_fn_proto,
|
||||
.inline_fn_proto,
|
||||
.inline_static_fn_proto,
|
||||
.fn_def,
|
||||
.static_fn_def,
|
||||
.inline_fn_def,
|
||||
.inline_static_fn_def,
|
||||
=> {
|
||||
try transFnDecl(c, node);
|
||||
},
|
||||
|
||||
.@"var",
|
||||
.static_var,
|
||||
.threadlocal_var,
|
||||
.threadlocal_static_var,
|
||||
.extern_var,
|
||||
.threadlocal_extern_var,
|
||||
=> {
|
||||
try transVarDecl(c, node, null);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void {
|
||||
@panic("TODO");
|
||||
}
|
||||
fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void {
|
||||
const raw_ty = c.tree.nodes.items(.ty)[@intFromEnum(fn_decl)];
|
||||
const fn_ty = raw_ty.canonicalize(.standard);
|
||||
const node_data = c.tree.nodes.items(.data)[@intFromEnum(fn_decl)];
|
||||
if (c.decl_table.get(@intFromPtr(fn_ty.data.func))) |_|
|
||||
return; // Avoid processing this decl twice
|
||||
|
||||
const fn_name = c.tree.tokSlice(node_data.decl.name);
|
||||
if (c.global_scope.sym_table.contains(fn_name))
|
||||
return; // Avoid processing this decl twice
|
||||
|
||||
const fn_decl_loc = 0; // TODO
|
||||
const has_body = node_data.decl.node != .none;
|
||||
const is_always_inline = has_body and raw_ty.getAttribute(.always_inline) != null;
|
||||
const proto_ctx = FnProtoContext{
|
||||
.fn_name = fn_name,
|
||||
.is_inline = is_always_inline,
|
||||
.is_extern = !has_body,
|
||||
.is_export = switch (c.tree.nodes.items(.tag)[@intFromEnum(fn_decl)]) {
|
||||
.fn_proto, .fn_def => has_body and !is_always_inline,
|
||||
|
||||
.inline_fn_proto, .inline_fn_def, .inline_static_fn_proto, .inline_static_fn_def, .static_fn_proto, .static_fn_def => false,
|
||||
|
||||
else => unreachable,
|
||||
},
|
||||
};
|
||||
|
||||
const proto_node = transFnType(c, &c.global_scope.base, raw_ty, fn_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
|
||||
if (!has_body) {
|
||||
return addTopLevelDecl(c, fn_name, proto_node);
|
||||
}
|
||||
const proto_payload = proto_node.castTag(.func).?;
|
||||
|
||||
// actual function definition with body
|
||||
const body_stmt = node_data.decl.node;
|
||||
var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
|
||||
block_scope.return_type = fn_ty.data.func.return_type;
|
||||
defer block_scope.deinit();
|
||||
|
||||
var scope = &block_scope.base;
|
||||
_ = &scope;
|
||||
|
||||
var param_id: c_uint = 0;
|
||||
for (proto_payload.data.params, fn_ty.data.func.params) |*param, param_info| {
|
||||
const param_name = param.name orelse {
|
||||
proto_payload.data.is_extern = true;
|
||||
proto_payload.data.is_export = false;
|
||||
proto_payload.data.is_inline = false;
|
||||
try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
|
||||
return addTopLevelDecl(c, fn_name, proto_node);
|
||||
};
|
||||
|
||||
const is_const = param_info.ty.qual.@"const";
|
||||
|
||||
const mangled_param_name = try block_scope.makeMangledName(c, param_name);
|
||||
param.name = mangled_param_name;
|
||||
|
||||
if (!is_const) {
|
||||
const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});
|
||||
const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
|
||||
param.name = arg_name;
|
||||
|
||||
const redecl_node = try ZigTag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
|
||||
try block_scope.statements.append(redecl_node);
|
||||
}
|
||||
try block_scope.discardVariable(c, mangled_param_name);
|
||||
|
||||
param_id += 1;
|
||||
}
|
||||
|
||||
transCompoundStmtInline(c, body_stmt, &block_scope) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
error.UnsupportedTranslation,
|
||||
error.UnsupportedType,
|
||||
=> {
|
||||
proto_payload.data.is_extern = true;
|
||||
proto_payload.data.is_export = false;
|
||||
proto_payload.data.is_inline = false;
|
||||
try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
|
||||
return addTopLevelDecl(c, fn_name, proto_node);
|
||||
},
|
||||
};
|
||||
|
||||
proto_payload.data.body = try block_scope.complete(c);
|
||||
return addTopLevelDecl(c, fn_name, proto_node);
|
||||
}
|
||||
|
||||
fn transVarDecl(_: *Context, _: NodeIndex, _: ?usize) Error!void {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: []const NodeIndex) Error!void {
|
||||
const node_types = c.tree.nodes.items(.ty);
|
||||
const ty = node_types[@intFromEnum(enum_decl)];
|
||||
if (c.decl_table.get(@intFromPtr(ty.data.@"enum"))) |_|
|
||||
return; // Avoid processing this decl twice
|
||||
const toplevel = scope.id == .root;
|
||||
const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
|
||||
|
||||
var is_unnamed = false;
|
||||
var bare_name: []const u8 = c.mapper.lookup(ty.data.@"enum".name);
|
||||
var name = bare_name;
|
||||
if (c.unnamed_typedefs.get(@intFromPtr(ty.data.@"enum"))) |typedef_name| {
|
||||
bare_name = typedef_name;
|
||||
name = typedef_name;
|
||||
} else {
|
||||
if (bare_name.len == 0) {
|
||||
bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
|
||||
is_unnamed = true;
|
||||
}
|
||||
name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
|
||||
}
|
||||
if (!toplevel) name = try bs.makeMangledName(c, name);
|
||||
try c.decl_table.putNoClobber(c.gpa, @intFromPtr(ty.data.@"enum"), name);
|
||||
|
||||
const enum_type_node = if (!ty.data.@"enum".isIncomplete()) blk: {
|
||||
for (ty.data.@"enum".fields, field_nodes) |field, field_node| {
|
||||
var enum_val_name: []const u8 = c.mapper.lookup(field.name);
|
||||
if (!toplevel) {
|
||||
enum_val_name = try bs.makeMangledName(c, enum_val_name);
|
||||
}
|
||||
|
||||
const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, field.name_tok) catch |err| switch (err) {
|
||||
error.UnsupportedType => null,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const val = c.tree.value_map.get(field_node).?;
|
||||
const enum_const_def = try ZigTag.enum_constant.create(c.arena, .{
|
||||
.name = enum_val_name,
|
||||
.is_public = toplevel,
|
||||
.type = enum_const_type_node,
|
||||
.value = try transCreateNodeAPInt(c, val),
|
||||
});
|
||||
if (toplevel)
|
||||
try addTopLevelDecl(c, enum_val_name, enum_const_def)
|
||||
else {
|
||||
try scope.appendNode(enum_const_def);
|
||||
try bs.discardVariable(c, enum_val_name);
|
||||
}
|
||||
}
|
||||
|
||||
break :blk transType(c, scope, ty.data.@"enum".tag_ty, 0) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
return failDecl(c, 0, name, "unable to translate enum integer type", .{});
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
} else blk: {
|
||||
try c.opaque_demotes.put(c.gpa, @intFromPtr(ty.data.@"enum"), {});
|
||||
break :blk ZigTag.opaque_literal.init();
|
||||
};
|
||||
|
||||
const is_pub = toplevel and !is_unnamed;
|
||||
const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = ([2]ZigTag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] },
|
||||
.data = .{
|
||||
.init = enum_type_node,
|
||||
.name = name,
|
||||
},
|
||||
};
|
||||
const node = ZigNode.initPayload(&payload.base);
|
||||
if (toplevel) {
|
||||
try addTopLevelDecl(c, name, node);
|
||||
if (!is_unnamed)
|
||||
try c.alias_list.append(.{ .alias = bare_name, .name = name });
|
||||
} else {
|
||||
try scope.appendNode(node);
|
||||
if (node.tag() != .pub_var_simple) {
|
||||
try bs.discardVariable(c, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode {
|
||||
const ty = raw_ty.canonicalize(.standard);
|
||||
switch (ty.specifier) {
|
||||
.void => return ZigTag.type.create(c.arena, "anyopaque"),
|
||||
.bool => return ZigTag.type.create(c.arena, "bool"),
|
||||
.char => return ZigTag.type.create(c.arena, "c_char"),
|
||||
.schar => return ZigTag.type.create(c.arena, "i8"),
|
||||
.uchar => return ZigTag.type.create(c.arena, "u8"),
|
||||
.short => return ZigTag.type.create(c.arena, "c_short"),
|
||||
.ushort => return ZigTag.type.create(c.arena, "c_ushort"),
|
||||
.int => return ZigTag.type.create(c.arena, "c_int"),
|
||||
.uint => return ZigTag.type.create(c.arena, "c_uint"),
|
||||
.long => return ZigTag.type.create(c.arena, "c_long"),
|
||||
.ulong => return ZigTag.type.create(c.arena, "c_ulong"),
|
||||
.long_long => return ZigTag.type.create(c.arena, "c_longlong"),
|
||||
.ulong_long => return ZigTag.type.create(c.arena, "c_ulonglong"),
|
||||
.int128 => return ZigTag.type.create(c.arena, "i128"),
|
||||
.uint128 => return ZigTag.type.create(c.arena, "u128"),
|
||||
.fp16, .float16 => return ZigTag.type.create(c.arena, "f16"),
|
||||
.float => return ZigTag.type.create(c.arena, "f32"),
|
||||
.double => return ZigTag.type.create(c.arena, "f64"),
|
||||
.long_double => return ZigTag.type.create(c.arena, "c_longdouble"),
|
||||
.float80 => return ZigTag.type.create(c.arena, "f80"),
|
||||
.float128 => return ZigTag.type.create(c.arena, "f128"),
|
||||
.func,
|
||||
.var_args_func,
|
||||
.old_style_func,
|
||||
=> return transFnType(c, scope, raw_ty, ty, source_loc, .{}),
|
||||
else => return error.UnsupportedType,
|
||||
}
|
||||
}
|
||||
|
||||
fn zigAlignment(bit_alignment: u29) u32 {
|
||||
return bit_alignment / 8;
|
||||
}
|
||||
|
||||
const FnProtoContext = struct {
|
||||
is_pub: bool = false,
|
||||
is_export: bool = false,
|
||||
is_extern: bool = false,
|
||||
is_inline: bool = false,
|
||||
fn_name: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
fn transFnType(
|
||||
c: *Context,
|
||||
scope: *Scope,
|
||||
raw_ty: Type,
|
||||
fn_ty: Type,
|
||||
source_loc: TokenIndex,
|
||||
ctx: FnProtoContext,
|
||||
) !ZigNode {
|
||||
const param_count: usize = fn_ty.data.func.params.len;
|
||||
const fn_params = try c.arena.alloc(ast.Payload.Param, param_count);
|
||||
|
||||
for (fn_ty.data.func.params, fn_params) |param_info, *param_node| {
|
||||
const param_ty = param_info.ty;
|
||||
const is_noalias = param_ty.qual.restrict;
|
||||
|
||||
const param_name: ?[]const u8 = if (param_info.name == .empty)
|
||||
null
|
||||
else
|
||||
c.mapper.lookup(param_info.name);
|
||||
|
||||
const type_node = try transType(c, scope, param_ty, param_info.name_tok);
|
||||
param_node.* = .{
|
||||
.is_noalias = is_noalias,
|
||||
.name = param_name,
|
||||
.type = type_node,
|
||||
};
|
||||
}
|
||||
|
||||
const linksection_string = blk: {
|
||||
if (raw_ty.getAttribute(.section)) |section| {
|
||||
break :blk c.comp.interner.get(section.name.ref()).bytes;
|
||||
}
|
||||
break :blk null;
|
||||
};
|
||||
|
||||
const alignment = if (raw_ty.requestedAlignment(c.comp)) |alignment| zigAlignment(alignment) else null;
|
||||
|
||||
const explicit_callconv = null;
|
||||
// const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc;
|
||||
|
||||
const return_type_node = blk: {
|
||||
if (raw_ty.getAttribute(.noreturn) != null) {
|
||||
break :blk ZigTag.noreturn_type.init();
|
||||
} else {
|
||||
const return_ty = fn_ty.data.func.return_type;
|
||||
if (return_ty.is(.void)) {
|
||||
// convert primitive anyopaque to actual void (only for return type)
|
||||
break :blk ZigTag.void_type.init();
|
||||
} else {
|
||||
break :blk transType(c, scope, return_ty, source_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
try warn(c, scope, source_loc, "unsupported function proto return type", .{});
|
||||
return err;
|
||||
},
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const payload = try c.arena.create(ast.Payload.Func);
|
||||
payload.* = .{
|
||||
.base = .{ .tag = .func },
|
||||
.data = .{
|
||||
.is_pub = ctx.is_pub,
|
||||
.is_extern = ctx.is_extern,
|
||||
.is_export = ctx.is_export,
|
||||
.is_inline = ctx.is_inline,
|
||||
.is_var_args = switch (fn_ty.specifier) {
|
||||
.func => false,
|
||||
.var_args_func => true,
|
||||
.old_style_func => !ctx.is_export and !ctx.is_inline,
|
||||
else => unreachable,
|
||||
},
|
||||
.name = ctx.fn_name,
|
||||
.linksection_string = linksection_string,
|
||||
.explicit_callconv = explicit_callconv,
|
||||
.params = fn_params,
|
||||
.return_type = return_type_node,
|
||||
.body = null,
|
||||
.alignment = alignment,
|
||||
},
|
||||
};
|
||||
return ZigNode.initPayload(&payload.base);
|
||||
}
|
||||
|
||||
fn transStmt(c: *Context, node: NodeIndex) TransError!ZigNode {
|
||||
return transExpr(c, node, .unused);
|
||||
}
|
||||
|
||||
fn transCompoundStmtInline(c: *Context, compound: NodeIndex, block: *Scope.Block) TransError!void {
|
||||
const data = c.tree.nodes.items(.data)[@intFromEnum(compound)];
|
||||
var buf: [2]NodeIndex = undefined;
|
||||
// TODO move these helpers to Aro
|
||||
const stmts = switch (c.tree.nodes.items(.tag)[@intFromEnum(compound)]) {
|
||||
.compound_stmt_two => blk: {
|
||||
if (data.bin.lhs != .none) buf[0] = data.bin.lhs;
|
||||
if (data.bin.rhs != .none) buf[1] = data.bin.rhs;
|
||||
break :blk buf[0 .. @as(u32, @intFromBool(data.bin.lhs != .none)) + @intFromBool(data.bin.rhs != .none)];
|
||||
},
|
||||
.compound_stmt => c.tree.data[data.range.start..data.range.end],
|
||||
else => unreachable,
|
||||
};
|
||||
for (stmts) |stmt| {
|
||||
const result = try transStmt(c, stmt);
|
||||
switch (result.tag()) {
|
||||
.declaration, .empty_block => {},
|
||||
else => try block.statements.append(result),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transCompoundStmt(c: *Context, scope: *Scope, compound: NodeIndex) TransError!ZigNode {
|
||||
var block_scope = try Scope.Block.init(c, scope, false);
|
||||
defer block_scope.deinit();
|
||||
try transCompoundStmtInline(c, compound, &block_scope);
|
||||
return try block_scope.complete(c);
|
||||
}
|
||||
|
||||
fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!ZigNode {
|
||||
std.debug.assert(node != .none);
|
||||
const ty = c.tree.nodes.items(.ty)[@intFromEnum(node)];
|
||||
if (c.tree.value_map.get(node)) |val| {
|
||||
// TODO handle other values
|
||||
const int = try transCreateNodeAPInt(c, val);
|
||||
const as_node = try ZigTag.as.create(c.arena, .{
|
||||
.lhs = try transType(c, undefined, ty, undefined),
|
||||
.rhs = int,
|
||||
});
|
||||
return maybeSuppressResult(c, result_used, as_node);
|
||||
}
|
||||
const node_tags = c.tree.nodes.items(.tag);
|
||||
switch (node_tags[@intFromEnum(node)]) {
|
||||
else => unreachable, // Not an expression.
|
||||
}
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn transCreateNodeAPInt(c: *Context, int: aro.Value) !ZigNode {
|
||||
var space: aro.Interner.Tag.Int.BigIntSpace = undefined;
|
||||
var big = int.toBigInt(&space, c.comp);
|
||||
const is_negative = !big.positive;
|
||||
big.positive = true;
|
||||
|
||||
const str = big.toStringAlloc(c.arena, 10, .lower) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
const res = try ZigTag.integer_literal.create(c.arena, str);
|
||||
if (is_negative) return ZigTag.negate.create(c.arena, res);
|
||||
return res;
|
||||
}
|
||||
121
src/main.zig
121
src/main.zig
|
|
@ -294,13 +294,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
|||
} else if (mem.eql(u8, cmd, "rc")) {
|
||||
return cmdRc(gpa, arena, args[1..]);
|
||||
} else if (mem.eql(u8, cmd, "fmt")) {
|
||||
return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig", false);
|
||||
return jitCmd(gpa, arena, cmd_args, .{
|
||||
.cmd_name = "fmt",
|
||||
.root_src_path = "fmt.zig",
|
||||
});
|
||||
} else if (mem.eql(u8, cmd, "objcopy")) {
|
||||
return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "fetch")) {
|
||||
return cmdFetch(gpa, arena, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "libc")) {
|
||||
return jitCmd(gpa, arena, cmd_args, "libc", "libc.zig", true);
|
||||
return jitCmd(gpa, arena, cmd_args, .{
|
||||
.cmd_name = "libc",
|
||||
.root_src_path = "libc.zig",
|
||||
.prepend_zig_lib_dir_path = true,
|
||||
});
|
||||
} else if (mem.eql(u8, cmd, "init")) {
|
||||
return cmdInit(gpa, arena, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "targets")) {
|
||||
|
|
@ -317,7 +324,10 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
|||
verifyLibcxxCorrectlyLinked();
|
||||
return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
|
||||
} else if (mem.eql(u8, cmd, "reduce")) {
|
||||
return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig", false);
|
||||
return jitCmd(gpa, arena, cmd_args, .{
|
||||
.cmd_name = "reduce",
|
||||
.root_src_path = "reduce.zig",
|
||||
});
|
||||
} else if (mem.eql(u8, cmd, "zen")) {
|
||||
return io.getStdOut().writeAll(info_zen);
|
||||
} else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
|
||||
|
|
@ -4459,7 +4469,13 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
|
|||
const digest = if (try man.hit()) man.final() else digest: {
|
||||
if (fancy_output) |p| p.cache_hit = false;
|
||||
var argv = std.ArrayList([]const u8).init(arena);
|
||||
try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1]
|
||||
switch (comp.config.c_frontend) {
|
||||
.aro => {},
|
||||
.clang => {
|
||||
// argv[0] is program name, actual args start at [1]
|
||||
try argv.append(@tagName(comp.config.c_frontend));
|
||||
},
|
||||
}
|
||||
|
||||
var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
|
||||
defer zig_cache_tmp_dir.close();
|
||||
|
|
@ -4484,24 +4500,18 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
|
|||
Compilation.dump_argv(argv.items);
|
||||
}
|
||||
|
||||
var tree = switch (comp.config.c_frontend) {
|
||||
.aro => tree: {
|
||||
const aro = @import("aro");
|
||||
const translate_c = @import("aro_translate_c.zig");
|
||||
var aro_comp = aro.Compilation.init(comp.gpa);
|
||||
defer aro_comp.deinit();
|
||||
|
||||
break :tree translate_c.translate(comp.gpa, &aro_comp, argv.items) catch |err| switch (err) {
|
||||
error.SemanticAnalyzeFail, error.FatalError => {
|
||||
// TODO convert these to zig errors
|
||||
aro.Diagnostics.render(&aro_comp, std.io.tty.detectConfig(std.io.getStdErr()));
|
||||
process.exit(1);
|
||||
},
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.StreamTooLong => fatal("StreamTooLong?", .{}),
|
||||
};
|
||||
const formatted = switch (comp.config.c_frontend) {
|
||||
.aro => f: {
|
||||
var stdout: []u8 = undefined;
|
||||
try jitCmd(comp.gpa, arena, argv.items, .{
|
||||
.cmd_name = "aro_translate_c",
|
||||
.root_src_path = "aro_translate_c.zig",
|
||||
.depend_on_aro = true,
|
||||
.capture = &stdout,
|
||||
});
|
||||
break :f stdout;
|
||||
},
|
||||
.clang => tree: {
|
||||
.clang => f: {
|
||||
if (!build_options.have_llvm) unreachable;
|
||||
const translate_c = @import("translate_c.zig");
|
||||
|
||||
|
|
@ -4519,7 +4529,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
|
|||
|
||||
const c_headers_dir_path_z = try comp.zig_lib_directory.joinZ(arena, &[_][]const u8{"include"});
|
||||
var errors = std.zig.ErrorBundle.empty;
|
||||
break :tree translate_c.translate(
|
||||
var tree = translate_c.translate(
|
||||
comp.gpa,
|
||||
new_argv.ptr,
|
||||
new_argv.ptr + new_argv.len,
|
||||
|
|
@ -4537,9 +4547,10 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
|
|||
}
|
||||
},
|
||||
};
|
||||
defer tree.deinit(comp.gpa);
|
||||
break :f try tree.render(arena);
|
||||
},
|
||||
};
|
||||
defer tree.deinit(comp.gpa);
|
||||
|
||||
if (out_dep_path) |dep_file_path| {
|
||||
const dep_basename = fs.path.basename(dep_file_path);
|
||||
|
|
@ -4560,9 +4571,6 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
|
|||
var zig_file = try o_dir.createFile(translated_zig_basename, .{});
|
||||
defer zig_file.close();
|
||||
|
||||
const formatted = try tree.render(comp.gpa);
|
||||
defer comp.gpa.free(formatted);
|
||||
|
||||
try zig_file.writeAll(formatted);
|
||||
|
||||
man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{
|
||||
|
|
@ -5522,13 +5530,19 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
|||
}
|
||||
}
|
||||
|
||||
const JitCmdOptions = struct {
|
||||
cmd_name: []const u8,
|
||||
root_src_path: []const u8,
|
||||
prepend_zig_lib_dir_path: bool = false,
|
||||
depend_on_aro: bool = false,
|
||||
capture: ?*[]u8 = null,
|
||||
};
|
||||
|
||||
fn jitCmd(
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
args: []const []const u8,
|
||||
cmd_name: []const u8,
|
||||
root_src_path: []const u8,
|
||||
prepend_zig_lib_dir_path: bool,
|
||||
options: JitCmdOptions,
|
||||
) !void {
|
||||
const color: Color = .auto;
|
||||
|
||||
|
|
@ -5540,7 +5554,7 @@ fn jitCmd(
|
|||
};
|
||||
|
||||
const exe_basename = try std.zig.binNameAlloc(arena, .{
|
||||
.root_name = cmd_name,
|
||||
.root_name = options.cmd_name,
|
||||
.target = resolved_target.result,
|
||||
.output_mode = .Exe,
|
||||
});
|
||||
|
|
@ -5595,7 +5609,7 @@ fn jitCmd(
|
|||
.root_dir = zig_lib_directory,
|
||||
.sub_path = "compiler",
|
||||
},
|
||||
.root_src_path = root_src_path,
|
||||
.root_src_path = options.root_src_path,
|
||||
};
|
||||
|
||||
const config = try Compilation.Config.resolve(.{
|
||||
|
|
@ -5623,11 +5637,35 @@ fn jitCmd(
|
|||
.builtin_mod = null,
|
||||
});
|
||||
|
||||
if (options.depend_on_aro) {
|
||||
const aro_mod = try Package.Module.create(arena, .{
|
||||
.global_cache_directory = global_cache_directory,
|
||||
.paths = .{
|
||||
.root = .{
|
||||
.root_dir = zig_lib_directory,
|
||||
.sub_path = "compiler/aro",
|
||||
},
|
||||
.root_src_path = "aro.zig",
|
||||
},
|
||||
.fully_qualified_name = "aro",
|
||||
.cc_argv = &.{},
|
||||
.inherited = .{
|
||||
.resolved_target = resolved_target,
|
||||
.optimize_mode = optimize_mode,
|
||||
.strip = strip,
|
||||
},
|
||||
.global = config,
|
||||
.parent = null,
|
||||
.builtin_mod = root_mod.getBuiltinDependency(),
|
||||
});
|
||||
try root_mod.deps.put(arena, "aro", aro_mod);
|
||||
}
|
||||
|
||||
const comp = Compilation.create(gpa, arena, .{
|
||||
.zig_lib_directory = zig_lib_directory,
|
||||
.local_cache_directory = global_cache_directory,
|
||||
.global_cache_directory = global_cache_directory,
|
||||
.root_name = cmd_name,
|
||||
.root_name = options.cmd_name,
|
||||
.config = config,
|
||||
.root_mod = root_mod,
|
||||
.main_mod = root_mod,
|
||||
|
|
@ -5650,12 +5688,12 @@ fn jitCmd(
|
|||
child_argv.appendAssumeCapacity(exe_path);
|
||||
}
|
||||
|
||||
if (prepend_zig_lib_dir_path)
|
||||
if (options.prepend_zig_lib_dir_path)
|
||||
child_argv.appendAssumeCapacity(zig_lib_directory.path.?);
|
||||
|
||||
child_argv.appendSliceAssumeCapacity(args);
|
||||
|
||||
if (process.can_execv) {
|
||||
if (process.can_execv and options.capture == null) {
|
||||
const err = process.execv(gpa, child_argv.items);
|
||||
const cmd = try std.mem.join(arena, " ", child_argv.items);
|
||||
fatal("the following command failed to execve with '{s}':\n{s}", .{
|
||||
|
|
@ -5673,13 +5711,22 @@ fn jitCmd(
|
|||
|
||||
var child = std.ChildProcess.init(child_argv.items, gpa);
|
||||
child.stdin_behavior = .Inherit;
|
||||
child.stdout_behavior = .Inherit;
|
||||
child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe;
|
||||
child.stderr_behavior = .Inherit;
|
||||
|
||||
const term = try child.spawnAndWait();
|
||||
try child.spawn();
|
||||
|
||||
if (options.capture) |ptr| {
|
||||
ptr.* = try child.stdout.?.readToEndAlloc(arena, std.math.maxInt(u32));
|
||||
}
|
||||
|
||||
const term = try child.wait();
|
||||
switch (term) {
|
||||
.Exited => |code| {
|
||||
if (code == 0) return cleanExit();
|
||||
if (code == 0) {
|
||||
if (options.capture != null) return;
|
||||
return cleanExit();
|
||||
}
|
||||
const cmd = try std.mem.join(arena, " ", child_argv.items);
|
||||
fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
//! Stub implementation only used when bootstrapping stage2
|
||||
//! Keep in sync with deps/aro/build/GenerateDef.zig
|
||||
|
||||
pub fn with(comptime Properties: type) type {
|
||||
return struct {
|
||||
tag: Tag = @enumFromInt(0),
|
||||
properties: Properties = undefined,
|
||||
pub const max_param_count = 1;
|
||||
pub const longest_name = 0;
|
||||
pub const data = [_]@This(){.{}};
|
||||
pub inline fn fromName(_: []const u8) ?@This() {
|
||||
return .{};
|
||||
}
|
||||
pub fn nameFromUniqueIndex(_: u16, _: []u8) []u8 {
|
||||
return "";
|
||||
}
|
||||
pub fn uniqueIndex(_: []const u8) ?u16 {
|
||||
return null;
|
||||
}
|
||||
pub const Tag = enum(u16) { _ };
|
||||
pub fn nameFromTag(_: Tag) NameBuf {
|
||||
return .{};
|
||||
}
|
||||
pub fn tagFromName(name: []const u8) ?Tag {
|
||||
var res: u16 = 0;
|
||||
for (name) |c| res +%= c;
|
||||
return @enumFromInt(res);
|
||||
}
|
||||
pub const NameBuf = struct {
|
||||
pub fn span(_: *const NameBuf) []const u8 {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,508 +0,0 @@
|
|||
//! Stub implementation only used when bootstrapping stage2
|
||||
//! Keep in sync with deps/aro/build/GenerateDef.zig
|
||||
|
||||
pub fn with(comptime Properties: type) type {
|
||||
return struct {
|
||||
pub const Tag = enum {
|
||||
todo,
|
||||
error_directive,
|
||||
warning_directive,
|
||||
elif_without_if,
|
||||
elif_after_else,
|
||||
elifdef_without_if,
|
||||
elifdef_after_else,
|
||||
elifndef_without_if,
|
||||
elifndef_after_else,
|
||||
else_without_if,
|
||||
else_after_else,
|
||||
endif_without_if,
|
||||
unknown_pragma,
|
||||
line_simple_digit,
|
||||
line_invalid_filename,
|
||||
unterminated_conditional_directive,
|
||||
invalid_preprocessing_directive,
|
||||
macro_name_missing,
|
||||
extra_tokens_directive_end,
|
||||
expected_value_in_expr,
|
||||
closing_paren,
|
||||
to_match_paren,
|
||||
to_match_brace,
|
||||
to_match_bracket,
|
||||
header_str_closing,
|
||||
header_str_match,
|
||||
string_literal_in_pp_expr,
|
||||
float_literal_in_pp_expr,
|
||||
defined_as_macro_name,
|
||||
macro_name_must_be_identifier,
|
||||
whitespace_after_macro_name,
|
||||
hash_hash_at_start,
|
||||
hash_hash_at_end,
|
||||
pasting_formed_invalid,
|
||||
missing_paren_param_list,
|
||||
unterminated_macro_param_list,
|
||||
invalid_token_param_list,
|
||||
expected_comma_param_list,
|
||||
hash_not_followed_param,
|
||||
expected_filename,
|
||||
empty_filename,
|
||||
expected_invalid,
|
||||
expected_eof,
|
||||
expected_token,
|
||||
expected_expr,
|
||||
expected_integer_constant_expr,
|
||||
missing_type_specifier,
|
||||
missing_type_specifier_c23,
|
||||
multiple_storage_class,
|
||||
static_assert_failure,
|
||||
static_assert_failure_message,
|
||||
expected_type,
|
||||
cannot_combine_spec,
|
||||
duplicate_decl_spec,
|
||||
restrict_non_pointer,
|
||||
expected_external_decl,
|
||||
expected_ident_or_l_paren,
|
||||
missing_declaration,
|
||||
func_not_in_root,
|
||||
illegal_initializer,
|
||||
extern_initializer,
|
||||
spec_from_typedef,
|
||||
param_before_var_args,
|
||||
void_only_param,
|
||||
void_param_qualified,
|
||||
void_must_be_first_param,
|
||||
invalid_storage_on_param,
|
||||
threadlocal_non_var,
|
||||
func_spec_non_func,
|
||||
illegal_storage_on_func,
|
||||
illegal_storage_on_global,
|
||||
expected_stmt,
|
||||
func_cannot_return_func,
|
||||
func_cannot_return_array,
|
||||
undeclared_identifier,
|
||||
not_callable,
|
||||
unsupported_str_cat,
|
||||
static_func_not_global,
|
||||
implicit_func_decl,
|
||||
unknown_builtin,
|
||||
implicit_builtin,
|
||||
implicit_builtin_header_note,
|
||||
expected_param_decl,
|
||||
invalid_old_style_params,
|
||||
expected_fn_body,
|
||||
invalid_void_param,
|
||||
unused_value,
|
||||
continue_not_in_loop,
|
||||
break_not_in_loop_or_switch,
|
||||
unreachable_code,
|
||||
duplicate_label,
|
||||
previous_label,
|
||||
undeclared_label,
|
||||
case_not_in_switch,
|
||||
duplicate_switch_case,
|
||||
multiple_default,
|
||||
previous_case,
|
||||
expected_arguments,
|
||||
expected_arguments_old,
|
||||
expected_at_least_arguments,
|
||||
invalid_static_star,
|
||||
static_non_param,
|
||||
array_qualifiers,
|
||||
star_non_param,
|
||||
variable_len_array_file_scope,
|
||||
useless_static,
|
||||
negative_array_size,
|
||||
array_incomplete_elem,
|
||||
array_func_elem,
|
||||
static_non_outermost_array,
|
||||
qualifier_non_outermost_array,
|
||||
unterminated_macro_arg_list,
|
||||
unknown_warning,
|
||||
overflow,
|
||||
int_literal_too_big,
|
||||
indirection_ptr,
|
||||
addr_of_rvalue,
|
||||
addr_of_bitfield,
|
||||
not_assignable,
|
||||
ident_or_l_brace,
|
||||
empty_enum,
|
||||
redefinition,
|
||||
previous_definition,
|
||||
expected_identifier,
|
||||
expected_str_literal,
|
||||
expected_str_literal_in,
|
||||
parameter_missing,
|
||||
empty_record,
|
||||
empty_record_size,
|
||||
wrong_tag,
|
||||
expected_parens_around_typename,
|
||||
alignof_expr,
|
||||
invalid_alignof,
|
||||
invalid_sizeof,
|
||||
macro_redefined,
|
||||
generic_qual_type,
|
||||
generic_array_type,
|
||||
generic_func_type,
|
||||
generic_duplicate,
|
||||
generic_duplicate_here,
|
||||
generic_duplicate_default,
|
||||
generic_no_match,
|
||||
escape_sequence_overflow,
|
||||
invalid_universal_character,
|
||||
incomplete_universal_character,
|
||||
multichar_literal_warning,
|
||||
invalid_multichar_literal,
|
||||
wide_multichar_literal,
|
||||
char_lit_too_wide,
|
||||
char_too_large,
|
||||
must_use_struct,
|
||||
must_use_union,
|
||||
must_use_enum,
|
||||
redefinition_different_sym,
|
||||
redefinition_incompatible,
|
||||
redefinition_of_parameter,
|
||||
invalid_bin_types,
|
||||
comparison_ptr_int,
|
||||
comparison_distinct_ptr,
|
||||
incompatible_pointers,
|
||||
invalid_argument_un,
|
||||
incompatible_assign,
|
||||
implicit_ptr_to_int,
|
||||
invalid_cast_to_float,
|
||||
invalid_cast_to_pointer,
|
||||
invalid_cast_type,
|
||||
qual_cast,
|
||||
invalid_index,
|
||||
invalid_subscript,
|
||||
array_after,
|
||||
array_before,
|
||||
statement_int,
|
||||
statement_scalar,
|
||||
func_should_return,
|
||||
incompatible_return,
|
||||
incompatible_return_sign,
|
||||
implicit_int_to_ptr,
|
||||
func_does_not_return,
|
||||
void_func_returns_value,
|
||||
incompatible_arg,
|
||||
incompatible_ptr_arg,
|
||||
incompatible_ptr_arg_sign,
|
||||
parameter_here,
|
||||
atomic_array,
|
||||
atomic_func,
|
||||
atomic_incomplete,
|
||||
addr_of_register,
|
||||
variable_incomplete_ty,
|
||||
parameter_incomplete_ty,
|
||||
tentative_array,
|
||||
deref_incomplete_ty_ptr,
|
||||
alignas_on_func,
|
||||
alignas_on_param,
|
||||
minimum_alignment,
|
||||
maximum_alignment,
|
||||
negative_alignment,
|
||||
align_ignored,
|
||||
zero_align_ignored,
|
||||
non_pow2_align,
|
||||
pointer_mismatch,
|
||||
static_assert_not_constant,
|
||||
static_assert_missing_message,
|
||||
pre_c23_compat,
|
||||
unbound_vla,
|
||||
array_too_large,
|
||||
incompatible_ptr_init,
|
||||
incompatible_ptr_init_sign,
|
||||
incompatible_ptr_assign,
|
||||
incompatible_ptr_assign_sign,
|
||||
vla_init,
|
||||
func_init,
|
||||
incompatible_init,
|
||||
empty_scalar_init,
|
||||
excess_scalar_init,
|
||||
excess_str_init,
|
||||
excess_struct_init,
|
||||
excess_array_init,
|
||||
str_init_too_long,
|
||||
arr_init_too_long,
|
||||
invalid_typeof,
|
||||
division_by_zero,
|
||||
division_by_zero_macro,
|
||||
builtin_choose_cond,
|
||||
alignas_unavailable,
|
||||
case_val_unavailable,
|
||||
enum_val_unavailable,
|
||||
incompatible_array_init,
|
||||
array_init_str,
|
||||
initializer_overrides,
|
||||
previous_initializer,
|
||||
invalid_array_designator,
|
||||
negative_array_designator,
|
||||
oob_array_designator,
|
||||
invalid_field_designator,
|
||||
no_such_field_designator,
|
||||
empty_aggregate_init_braces,
|
||||
ptr_init_discards_quals,
|
||||
ptr_assign_discards_quals,
|
||||
ptr_ret_discards_quals,
|
||||
ptr_arg_discards_quals,
|
||||
unknown_attribute,
|
||||
ignored_attribute,
|
||||
invalid_fallthrough,
|
||||
cannot_apply_attribute_to_statement,
|
||||
builtin_macro_redefined,
|
||||
feature_check_requires_identifier,
|
||||
missing_tok_builtin,
|
||||
gnu_label_as_value,
|
||||
expected_record_ty,
|
||||
member_expr_not_ptr,
|
||||
member_expr_ptr,
|
||||
no_such_member,
|
||||
malformed_warning_check,
|
||||
invalid_computed_goto,
|
||||
pragma_warning_message,
|
||||
pragma_error_message,
|
||||
pragma_message,
|
||||
pragma_requires_string_literal,
|
||||
poisoned_identifier,
|
||||
pragma_poison_identifier,
|
||||
pragma_poison_macro,
|
||||
newline_eof,
|
||||
empty_translation_unit,
|
||||
omitting_parameter_name,
|
||||
non_int_bitfield,
|
||||
negative_bitwidth,
|
||||
zero_width_named_field,
|
||||
bitfield_too_big,
|
||||
invalid_utf8,
|
||||
implicitly_unsigned_literal,
|
||||
invalid_preproc_operator,
|
||||
invalid_preproc_expr_start,
|
||||
c99_compat,
|
||||
unexpected_character,
|
||||
invalid_identifier_start_char,
|
||||
unicode_zero_width,
|
||||
unicode_homoglyph,
|
||||
meaningless_asm_qual,
|
||||
duplicate_asm_qual,
|
||||
invalid_asm_str,
|
||||
dollar_in_identifier_extension,
|
||||
dollars_in_identifiers,
|
||||
expanded_from_here,
|
||||
skipping_macro_backtrace,
|
||||
pragma_operator_string_literal,
|
||||
unknown_gcc_pragma,
|
||||
unknown_gcc_pragma_directive,
|
||||
predefined_top_level,
|
||||
incompatible_va_arg,
|
||||
too_many_scalar_init_braces,
|
||||
uninitialized_in_own_init,
|
||||
gnu_statement_expression,
|
||||
stmt_expr_not_allowed_file_scope,
|
||||
gnu_imaginary_constant,
|
||||
plain_complex,
|
||||
complex_int,
|
||||
qual_on_ret_type,
|
||||
cli_invalid_standard,
|
||||
cli_invalid_target,
|
||||
cli_invalid_emulate,
|
||||
cli_unknown_arg,
|
||||
cli_error,
|
||||
cli_unused_link_object,
|
||||
cli_unknown_linker,
|
||||
extra_semi,
|
||||
func_field,
|
||||
vla_field,
|
||||
field_incomplete_ty,
|
||||
flexible_in_union,
|
||||
flexible_non_final,
|
||||
flexible_in_empty,
|
||||
duplicate_member,
|
||||
binary_integer_literal,
|
||||
gnu_va_macro,
|
||||
builtin_must_be_called,
|
||||
va_start_not_in_func,
|
||||
va_start_fixed_args,
|
||||
va_start_not_last_param,
|
||||
attribute_not_enough_args,
|
||||
attribute_too_many_args,
|
||||
attribute_arg_invalid,
|
||||
unknown_attr_enum,
|
||||
attribute_requires_identifier,
|
||||
declspec_not_enabled,
|
||||
declspec_attr_not_supported,
|
||||
deprecated_declarations,
|
||||
deprecated_note,
|
||||
unavailable,
|
||||
unavailable_note,
|
||||
warning_attribute,
|
||||
error_attribute,
|
||||
ignored_record_attr,
|
||||
backslash_newline_escape,
|
||||
array_size_non_int,
|
||||
cast_to_smaller_int,
|
||||
gnu_switch_range,
|
||||
empty_case_range,
|
||||
non_standard_escape_char,
|
||||
invalid_pp_stringify_escape,
|
||||
vla,
|
||||
float_overflow_conversion,
|
||||
float_out_of_range,
|
||||
float_zero_conversion,
|
||||
float_value_changed,
|
||||
float_to_int,
|
||||
const_decl_folded,
|
||||
const_decl_folded_vla,
|
||||
redefinition_of_typedef,
|
||||
undefined_macro,
|
||||
fn_macro_undefined,
|
||||
preprocessing_directive_only,
|
||||
missing_lparen_after_builtin,
|
||||
offsetof_ty,
|
||||
offsetof_incomplete,
|
||||
offsetof_array,
|
||||
pragma_pack_lparen,
|
||||
pragma_pack_rparen,
|
||||
pragma_pack_unknown_action,
|
||||
pragma_pack_show,
|
||||
pragma_pack_int,
|
||||
pragma_pack_int_ident,
|
||||
pragma_pack_undefined_pop,
|
||||
pragma_pack_empty_stack,
|
||||
cond_expr_type,
|
||||
too_many_includes,
|
||||
enumerator_too_small,
|
||||
enumerator_too_large,
|
||||
include_next,
|
||||
include_next_outside_header,
|
||||
enumerator_overflow,
|
||||
enum_not_representable,
|
||||
enum_too_large,
|
||||
enum_fixed,
|
||||
enum_prev_nonfixed,
|
||||
enum_prev_fixed,
|
||||
enum_different_explicit_ty,
|
||||
enum_not_representable_fixed,
|
||||
transparent_union_wrong_type,
|
||||
transparent_union_one_field,
|
||||
transparent_union_size,
|
||||
transparent_union_size_note,
|
||||
designated_init_invalid,
|
||||
designated_init_needed,
|
||||
ignore_common,
|
||||
ignore_nocommon,
|
||||
non_string_ignored,
|
||||
local_variable_attribute,
|
||||
ignore_cold,
|
||||
ignore_hot,
|
||||
ignore_noinline,
|
||||
ignore_always_inline,
|
||||
invalid_noreturn,
|
||||
nodiscard_unused,
|
||||
warn_unused_result,
|
||||
invalid_vec_elem_ty,
|
||||
vec_size_not_multiple,
|
||||
invalid_imag,
|
||||
invalid_real,
|
||||
zero_length_array,
|
||||
old_style_flexible_struct,
|
||||
comma_deletion_va_args,
|
||||
main_return_type,
|
||||
expansion_to_defined,
|
||||
invalid_int_suffix,
|
||||
invalid_float_suffix,
|
||||
invalid_octal_digit,
|
||||
invalid_binary_digit,
|
||||
exponent_has_no_digits,
|
||||
hex_floating_constant_requires_exponent,
|
||||
sizeof_returns_zero,
|
||||
declspec_not_allowed_after_declarator,
|
||||
declarator_name_tok,
|
||||
type_not_supported_on_target,
|
||||
bit_int,
|
||||
unsigned_bit_int_too_small,
|
||||
signed_bit_int_too_small,
|
||||
bit_int_too_big,
|
||||
keyword_macro,
|
||||
ptr_arithmetic_incomplete,
|
||||
callconv_not_supported,
|
||||
pointer_arith_void,
|
||||
sizeof_array_arg,
|
||||
array_address_to_bool,
|
||||
string_literal_to_bool,
|
||||
constant_expression_conversion_not_allowed,
|
||||
invalid_object_cast,
|
||||
cli_invalid_fp_eval_method,
|
||||
suggest_pointer_for_invalid_fp16,
|
||||
bitint_suffix,
|
||||
auto_type_extension,
|
||||
auto_type_not_allowed,
|
||||
auto_type_requires_initializer,
|
||||
auto_type_requires_single_declarator,
|
||||
auto_type_requires_plain_declarator,
|
||||
invalid_cast_to_auto_type,
|
||||
auto_type_from_bitfield,
|
||||
array_of_auto_type,
|
||||
auto_type_with_init_list,
|
||||
missing_semicolon,
|
||||
tentative_definition_incomplete,
|
||||
forward_declaration_here,
|
||||
gnu_union_cast,
|
||||
invalid_union_cast,
|
||||
cast_to_incomplete_type,
|
||||
invalid_source_epoch,
|
||||
fuse_ld_path,
|
||||
invalid_rtlib,
|
||||
unsupported_rtlib_gcc,
|
||||
invalid_unwindlib,
|
||||
incompatible_unwindlib,
|
||||
gnu_asm_disabled,
|
||||
extension_token_used,
|
||||
complex_component_init,
|
||||
complex_prefix_postfix_op,
|
||||
not_floating_type,
|
||||
argument_types_differ,
|
||||
ms_search_rule,
|
||||
ctrl_z_eof,
|
||||
illegal_char_encoding_warning,
|
||||
illegal_char_encoding_error,
|
||||
ucn_basic_char_error,
|
||||
ucn_basic_char_warning,
|
||||
ucn_control_char_error,
|
||||
ucn_control_char_warning,
|
||||
c89_ucn_in_literal,
|
||||
four_char_char_literal,
|
||||
multi_char_char_literal,
|
||||
missing_hex_escape,
|
||||
unknown_escape_sequence,
|
||||
attribute_requires_string,
|
||||
unterminated_string_literal_warning,
|
||||
unterminated_string_literal_error,
|
||||
empty_char_literal_warning,
|
||||
empty_char_literal_error,
|
||||
unterminated_char_literal_warning,
|
||||
unterminated_char_literal_error,
|
||||
unterminated_comment,
|
||||
def_no_proto_deprecated,
|
||||
passing_args_to_kr,
|
||||
unknown_type_name,
|
||||
label_compound_end,
|
||||
u8_char_lit,
|
||||
malformed_embed_param,
|
||||
malformed_embed_limit,
|
||||
duplicate_embed_param,
|
||||
unsupported_embed_param,
|
||||
invalid_compound_literal_storage_class,
|
||||
va_opt_lparen,
|
||||
va_opt_rparen,
|
||||
attribute_int_out_of_range,
|
||||
identifier_not_normalized,
|
||||
c23_auto_plain_declarator,
|
||||
c23_auto_single_declarator,
|
||||
c32_auto_requires_initializer,
|
||||
c23_auto_scalar_init,
|
||||
|
||||
pub fn property(_: Tag) Properties {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
//! Stub implementation only used when bootstrapping stage2
|
||||
//! Keep in sync with deps/aro/build/GenerateDef.zig
|
||||
|
||||
pub fn with(comptime _: type) type {
|
||||
return struct {
|
||||
pub inline fn fromName(_: []const u8) ?@This() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
pub const version_str: []const u8 = "bootstrap-stub";
|
||||
|
|
@ -8,10 +8,10 @@ const CallingConvention = std.builtin.CallingConvention;
|
|||
const clang = @import("clang.zig");
|
||||
const aro = @import("aro");
|
||||
const CToken = aro.Tokenizer.Token;
|
||||
const ast = @import("translate_c/ast.zig");
|
||||
const Node = ast.Node;
|
||||
const Tag = Node.Tag;
|
||||
const common = @import("translate_c/common.zig");
|
||||
const common = @import("aro_translate_c");
|
||||
const ast = common.ast;
|
||||
const Error = common.Error;
|
||||
const MacroProcessingError = common.MacroProcessingError;
|
||||
const TypeError = common.TypeError;
|
||||
|
|
@ -20,10 +20,8 @@ const SymbolTable = common.SymbolTable;
|
|||
const AliasList = common.AliasList;
|
||||
const ResultUsed = common.ResultUsed;
|
||||
const Scope = common.ScopeExtra(Context, clang.QualType);
|
||||
|
||||
// Maps macro parameter names to token position, for determining if different
|
||||
// identifiers refer to the same positional argument in different macros.
|
||||
const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
|
||||
const PatternList = common.PatternList;
|
||||
const MacroSlicer = common.MacroSlicer;
|
||||
|
||||
pub const Context = struct {
|
||||
gpa: mem.Allocator,
|
||||
|
|
@ -5093,265 +5091,6 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti
|
|||
try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment));
|
||||
}
|
||||
|
||||
pub const PatternList = struct {
|
||||
patterns: []Pattern,
|
||||
|
||||
/// Templates must be function-like macros
|
||||
/// first element is macro source, second element is the name of the function
|
||||
/// in std.lib.zig.c_translation.Macros which implements it
|
||||
const templates = [_][2][]const u8{
|
||||
[2][]const u8{ "f_SUFFIX(X) (X ## f)", "F_SUFFIX" },
|
||||
[2][]const u8{ "F_SUFFIX(X) (X ## F)", "F_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "u_SUFFIX(X) (X ## u)", "U_SUFFIX" },
|
||||
[2][]const u8{ "U_SUFFIX(X) (X ## U)", "U_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "l_SUFFIX(X) (X ## l)", "L_SUFFIX" },
|
||||
[2][]const u8{ "L_SUFFIX(X) (X ## L)", "L_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ul_SUFFIX(X) (X ## ul)", "UL_SUFFIX" },
|
||||
[2][]const u8{ "uL_SUFFIX(X) (X ## uL)", "UL_SUFFIX" },
|
||||
[2][]const u8{ "Ul_SUFFIX(X) (X ## Ul)", "UL_SUFFIX" },
|
||||
[2][]const u8{ "UL_SUFFIX(X) (X ## UL)", "UL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ll_SUFFIX(X) (X ## ll)", "LL_SUFFIX" },
|
||||
[2][]const u8{ "LL_SUFFIX(X) (X ## LL)", "LL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ull_SUFFIX(X) (X ## ull)", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "uLL_SUFFIX(X) (X ## uLL)", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "f_SUFFIX(X) X ## f", "F_SUFFIX" },
|
||||
[2][]const u8{ "F_SUFFIX(X) X ## F", "F_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "u_SUFFIX(X) X ## u", "U_SUFFIX" },
|
||||
[2][]const u8{ "U_SUFFIX(X) X ## U", "U_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "l_SUFFIX(X) X ## l", "L_SUFFIX" },
|
||||
[2][]const u8{ "L_SUFFIX(X) X ## L", "L_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ul_SUFFIX(X) X ## ul", "UL_SUFFIX" },
|
||||
[2][]const u8{ "uL_SUFFIX(X) X ## uL", "UL_SUFFIX" },
|
||||
[2][]const u8{ "Ul_SUFFIX(X) X ## Ul", "UL_SUFFIX" },
|
||||
[2][]const u8{ "UL_SUFFIX(X) X ## UL", "UL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ll_SUFFIX(X) X ## ll", "LL_SUFFIX" },
|
||||
[2][]const u8{ "LL_SUFFIX(X) X ## LL", "LL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "ull_SUFFIX(X) X ## ull", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "uLL_SUFFIX(X) X ## uLL", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "Ull_SUFFIX(X) X ## Ull", "ULL_SUFFIX" },
|
||||
[2][]const u8{ "ULL_SUFFIX(X) X ## ULL", "ULL_SUFFIX" },
|
||||
|
||||
[2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" },
|
||||
[2][]const u8{ "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL" },
|
||||
|
||||
[2][]const u8{
|
||||
\\wl_container_of(ptr, sample, member) \
|
||||
\\(__typeof__(sample))((char *)(ptr) - \
|
||||
\\ offsetof(__typeof__(*sample), member))
|
||||
,
|
||||
"WL_CONTAINER_OF",
|
||||
},
|
||||
|
||||
[2][]const u8{ "IGNORE_ME(X) ((void)(X))", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) (void)(X)", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) ((const void)(X))", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) (const void)(X)", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) ((volatile void)(X))", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) (volatile void)(X)", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) ((const volatile void)(X))", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) (const volatile void)(X)", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) ((volatile const void)(X))", "DISCARD" },
|
||||
[2][]const u8{ "IGNORE_ME(X) (volatile const void)(X)", "DISCARD" },
|
||||
};
|
||||
|
||||
/// Assumes that `ms` represents a tokenized function-like macro.
|
||||
fn buildArgsHash(allocator: mem.Allocator, ms: MacroSlicer, hash: *ArgsPositionMap) MacroProcessingError!void {
|
||||
assert(ms.tokens.len > 2);
|
||||
assert(ms.tokens[0].id == .identifier or ms.tokens[0].id == .extended_identifier);
|
||||
assert(ms.tokens[1].id == .l_paren);
|
||||
|
||||
var i: usize = 2;
|
||||
while (true) : (i += 1) {
|
||||
const token = ms.tokens[i];
|
||||
switch (token.id) {
|
||||
.r_paren => break,
|
||||
.comma => continue,
|
||||
.identifier, .extended_identifier => {
|
||||
const identifier = ms.slice(token);
|
||||
try hash.put(allocator, identifier, i);
|
||||
},
|
||||
else => return error.UnexpectedMacroToken,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Pattern = struct {
|
||||
tokens: []const CToken,
|
||||
source: []const u8,
|
||||
impl: []const u8,
|
||||
args_hash: ArgsPositionMap,
|
||||
|
||||
fn init(self: *Pattern, allocator: mem.Allocator, template: [2][]const u8) Error!void {
|
||||
const source = template[0];
|
||||
const impl = template[1];
|
||||
|
||||
var tok_list = std.ArrayList(CToken).init(allocator);
|
||||
defer tok_list.deinit();
|
||||
try tokenizeMacro(source, &tok_list);
|
||||
const tokens = try allocator.dupe(CToken, tok_list.items);
|
||||
|
||||
self.* = .{
|
||||
.tokens = tokens,
|
||||
.source = source,
|
||||
.impl = impl,
|
||||
.args_hash = .{},
|
||||
};
|
||||
const ms = MacroSlicer{ .source = source, .tokens = tokens };
|
||||
buildArgsHash(allocator, ms, &self.args_hash) catch |err| switch (err) {
|
||||
error.UnexpectedMacroToken => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *Pattern, allocator: mem.Allocator) void {
|
||||
self.args_hash.deinit(allocator);
|
||||
allocator.free(self.tokens);
|
||||
}
|
||||
|
||||
/// This function assumes that `ms` has already been validated to contain a function-like
|
||||
/// macro, and that the parsed template macro in `self` also contains a function-like
|
||||
/// macro. Please review this logic carefully if changing that assumption. Two
|
||||
/// function-like macros are considered equivalent if and only if they contain the same
|
||||
/// list of tokens, modulo parameter names.
|
||||
pub fn isEquivalent(self: Pattern, ms: MacroSlicer, args_hash: ArgsPositionMap) bool {
|
||||
if (self.tokens.len != ms.tokens.len) return false;
|
||||
if (args_hash.count() != self.args_hash.count()) return false;
|
||||
|
||||
var i: usize = 2;
|
||||
while (self.tokens[i].id != .r_paren) : (i += 1) {}
|
||||
|
||||
const pattern_slicer = MacroSlicer{ .source = self.source, .tokens = self.tokens };
|
||||
while (i < self.tokens.len) : (i += 1) {
|
||||
const pattern_token = self.tokens[i];
|
||||
const macro_token = ms.tokens[i];
|
||||
if (pattern_token.id != macro_token.id) return false;
|
||||
|
||||
const pattern_bytes = pattern_slicer.slice(pattern_token);
|
||||
const macro_bytes = ms.slice(macro_token);
|
||||
switch (pattern_token.id) {
|
||||
.identifier, .extended_identifier => {
|
||||
const pattern_arg_index = self.args_hash.get(pattern_bytes);
|
||||
const macro_arg_index = args_hash.get(macro_bytes);
|
||||
|
||||
if (pattern_arg_index == null and macro_arg_index == null) {
|
||||
if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
|
||||
} else if (pattern_arg_index != null and macro_arg_index != null) {
|
||||
if (pattern_arg_index.? != macro_arg_index.?) return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
.string_literal, .char_literal, .pp_num => {
|
||||
if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
|
||||
},
|
||||
else => {
|
||||
// other tags correspond to keywords and operators that do not contain a "payload"
|
||||
// that can vary
|
||||
},
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(allocator: mem.Allocator) Error!PatternList {
|
||||
const patterns = try allocator.alloc(Pattern, templates.len);
|
||||
for (templates, 0..) |template, i| {
|
||||
try patterns[i].init(allocator, template);
|
||||
}
|
||||
return PatternList{ .patterns = patterns };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PatternList, allocator: mem.Allocator) void {
|
||||
for (self.patterns) |*pattern| pattern.deinit(allocator);
|
||||
allocator.free(self.patterns);
|
||||
}
|
||||
|
||||
pub fn match(self: PatternList, allocator: mem.Allocator, ms: MacroSlicer) Error!?Pattern {
|
||||
var args_hash: ArgsPositionMap = .{};
|
||||
defer args_hash.deinit(allocator);
|
||||
|
||||
buildArgsHash(allocator, ms, &args_hash) catch |err| switch (err) {
|
||||
error.UnexpectedMacroToken => return null,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
for (self.patterns) |pattern| if (pattern.isEquivalent(ms, args_hash)) return pattern;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const MacroSlicer = struct {
|
||||
source: []const u8,
|
||||
tokens: []const CToken,
|
||||
fn slice(self: MacroSlicer, token: CToken) []const u8 {
|
||||
return self.source[token.start..token.end];
|
||||
}
|
||||
};
|
||||
|
||||
// Testing here instead of test/translate_c.zig allows us to also test that the
|
||||
// mapped function exists in `std.zig.c_translation.Macros`
|
||||
test "Macro matching" {
|
||||
const helper = struct {
|
||||
const MacroFunctions = std.zig.c_translation.Macros;
|
||||
fn checkMacro(allocator: mem.Allocator, pattern_list: PatternList, source: []const u8, comptime expected_match: ?[]const u8) !void {
|
||||
var tok_list = std.ArrayList(CToken).init(allocator);
|
||||
defer tok_list.deinit();
|
||||
try tokenizeMacro(source, &tok_list);
|
||||
const macro_slicer = MacroSlicer{ .source = source, .tokens = tok_list.items };
|
||||
const matched = try pattern_list.match(allocator, macro_slicer);
|
||||
if (expected_match) |expected| {
|
||||
try testing.expectEqualStrings(expected, matched.?.impl);
|
||||
try testing.expect(@hasDecl(MacroFunctions, expected));
|
||||
} else {
|
||||
try testing.expectEqual(@as(@TypeOf(matched), null), matched);
|
||||
}
|
||||
}
|
||||
};
|
||||
const allocator = std.testing.allocator;
|
||||
var pattern_list = try PatternList.init(allocator);
|
||||
defer pattern_list.deinit(allocator);
|
||||
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", "F_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", "U_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", "L_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", "LL_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", "UL_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", "ULL_SUFFIX");
|
||||
try helper.checkMacro(allocator, pattern_list,
|
||||
\\container_of(a, b, c) \
|
||||
\\(__typeof__(b))((char *)(a) - \
|
||||
\\ offsetof(__typeof__(*b), c))
|
||||
, "WL_CONTAINER_OF");
|
||||
|
||||
try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
|
||||
try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL");
|
||||
try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (void)(X)", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((void)(X))", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const void)(X)", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const void)(X))", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile void)(X)", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile void)(X))", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const volatile void)(X)", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const volatile void)(X))", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile const void)(X)", "DISCARD");
|
||||
try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile const void)(X))", "DISCARD");
|
||||
}
|
||||
|
||||
const MacroCtx = struct {
|
||||
source: []const u8,
|
||||
list: []const CToken,
|
||||
|
|
@ -5392,7 +5131,7 @@ const MacroCtx = struct {
|
|||
}
|
||||
|
||||
fn makeSlicer(self: *const MacroCtx) MacroSlicer {
|
||||
return MacroSlicer{ .source = self.source, .tokens = self.list };
|
||||
return .{ .source = self.source, .tokens = self.list };
|
||||
}
|
||||
|
||||
const MacroTranslateError = union(enum) {
|
||||
|
|
@ -5432,26 +5171,6 @@ const MacroCtx = struct {
|
|||
}
|
||||
};
|
||||
|
||||
fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!void {
|
||||
var tokenizer: aro.Tokenizer = .{
|
||||
.buf = source,
|
||||
.source = .unused,
|
||||
.langopts = .{},
|
||||
};
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
switch (tok.id) {
|
||||
.whitespace => continue,
|
||||
.nl, .eof => {
|
||||
try tok_list.append(tok);
|
||||
break;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
try tok_list.append(tok);
|
||||
}
|
||||
}
|
||||
|
||||
fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) ![]const u8 {
|
||||
const begin_loc = macro.getSourceRange_getBegin();
|
||||
const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit);
|
||||
|
|
@ -5491,7 +5210,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
|
|||
|
||||
const source = try getMacroText(unit, c, macro);
|
||||
|
||||
try tokenizeMacro(source, &tok_list);
|
||||
try common.tokenizeMacro(source, &tok_list);
|
||||
|
||||
var macro_ctx = MacroCtx{
|
||||
.source = source,
|
||||
|
|
|
|||
|
|
@ -1,322 +0,0 @@
|
|||
const std = @import("std");
|
||||
const ast = @import("ast.zig");
|
||||
const Node = ast.Node;
|
||||
const Tag = Node.Tag;
|
||||
|
||||
const CallingConvention = std.builtin.CallingConvention;
|
||||
|
||||
pub const Error = std.mem.Allocator.Error;
|
||||
pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
|
||||
pub const TypeError = Error || error{UnsupportedType};
|
||||
pub const TransError = TypeError || error{UnsupportedTranslation};
|
||||
|
||||
pub const SymbolTable = std.StringArrayHashMap(Node);
|
||||
pub const AliasList = std.ArrayList(struct {
|
||||
alias: []const u8,
|
||||
name: []const u8,
|
||||
});
|
||||
|
||||
pub const ResultUsed = enum {
|
||||
used,
|
||||
unused,
|
||||
};
|
||||
|
||||
pub fn ScopeExtra(comptime Context: type, comptime Type: type) type {
|
||||
return struct {
|
||||
id: Id,
|
||||
parent: ?*Scope,
|
||||
|
||||
const Scope = @This();
|
||||
|
||||
pub const Id = enum {
|
||||
block,
|
||||
root,
|
||||
condition,
|
||||
loop,
|
||||
do_loop,
|
||||
};
|
||||
|
||||
/// Used for the scope of condition expressions, for example `if (cond)`.
|
||||
/// The block is lazily initialised because it is only needed for rare
|
||||
/// cases of comma operators being used.
|
||||
pub const Condition = struct {
|
||||
base: Scope,
|
||||
block: ?Block = null,
|
||||
|
||||
pub fn getBlockScope(self: *Condition, c: *Context) !*Block {
|
||||
if (self.block) |*b| return b;
|
||||
self.block = try Block.init(c, &self.base, true);
|
||||
return &self.block.?;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Condition) void {
|
||||
if (self.block) |*b| b.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents an in-progress Node.Block. This struct is stack-allocated.
|
||||
/// When it is deinitialized, it produces an Node.Block which is allocated
|
||||
/// into the main arena.
|
||||
pub const Block = struct {
|
||||
base: Scope,
|
||||
statements: std.ArrayList(Node),
|
||||
variables: AliasList,
|
||||
mangle_count: u32 = 0,
|
||||
label: ?[]const u8 = null,
|
||||
|
||||
/// By default all variables are discarded, since we do not know in advance if they
|
||||
/// will be used. This maps the variable's name to the Discard payload, so that if
|
||||
/// the variable is subsequently referenced we can indicate that the discard should
|
||||
/// be skipped during the intermediate AST -> Zig AST render step.
|
||||
variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
|
||||
|
||||
/// When the block corresponds to a function, keep track of the return type
|
||||
/// so that the return expression can be cast, if necessary
|
||||
return_type: ?Type = null,
|
||||
|
||||
/// C static local variables are wrapped in a block-local struct. The struct
|
||||
/// is named after the (mangled) variable name, the Zig variable within the
|
||||
/// struct itself is given this name.
|
||||
pub const static_inner_name = "static";
|
||||
|
||||
pub fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
|
||||
var blk = Block{
|
||||
.base = .{
|
||||
.id = .block,
|
||||
.parent = parent,
|
||||
},
|
||||
.statements = std.ArrayList(Node).init(c.gpa),
|
||||
.variables = AliasList.init(c.gpa),
|
||||
.variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
|
||||
};
|
||||
if (labeled) {
|
||||
blk.label = try blk.makeMangledName(c, "blk");
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Block) void {
|
||||
self.statements.deinit();
|
||||
self.variables.deinit();
|
||||
self.variable_discards.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn complete(self: *Block, c: *Context) !Node {
|
||||
if (self.base.parent.?.id == .do_loop) {
|
||||
// We reserve 1 extra statement if the parent is a do_loop. This is in case of
|
||||
// do while, we want to put `if (cond) break;` at the end.
|
||||
const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop);
|
||||
var stmts = try c.arena.alloc(Node, alloc_len);
|
||||
stmts.len = self.statements.items.len;
|
||||
@memcpy(stmts[0..self.statements.items.len], self.statements.items);
|
||||
return Tag.block.create(c.arena, .{
|
||||
.label = self.label,
|
||||
.stmts = stmts,
|
||||
});
|
||||
}
|
||||
if (self.statements.items.len == 0) return Tag.empty_block.init();
|
||||
return Tag.block.create(c.arena, .{
|
||||
.label = self.label,
|
||||
.stmts = try c.arena.dupe(Node, self.statements.items),
|
||||
});
|
||||
}
|
||||
|
||||
/// Given the desired name, return a name that does not shadow anything from outer scopes.
|
||||
/// Inserts the returned name into the scope.
|
||||
/// The name will not be visible to callers of getAlias.
|
||||
pub fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
|
||||
return scope.createMangledName(c, name, true);
|
||||
}
|
||||
|
||||
/// Same as reserveMangledName, but enables the alias immediately.
|
||||
pub fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
|
||||
return scope.createMangledName(c, name, false);
|
||||
}
|
||||
|
||||
pub fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
|
||||
const name_copy = try c.arena.dupe(u8, name);
|
||||
var proposed_name = name_copy;
|
||||
while (scope.contains(proposed_name)) {
|
||||
scope.mangle_count += 1;
|
||||
proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
|
||||
}
|
||||
const new_mangle = try scope.variables.addOne();
|
||||
if (reservation) {
|
||||
new_mangle.* = .{ .name = name_copy, .alias = name_copy };
|
||||
} else {
|
||||
new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
|
||||
}
|
||||
return proposed_name;
|
||||
}
|
||||
|
||||
pub fn getAlias(scope: *Block, name: []const u8) []const u8 {
|
||||
for (scope.variables.items) |p| {
|
||||
if (std.mem.eql(u8, p.name, name))
|
||||
return p.alias;
|
||||
}
|
||||
return scope.base.parent.?.getAlias(name);
|
||||
}
|
||||
|
||||
pub fn localContains(scope: *Block, name: []const u8) bool {
|
||||
for (scope.variables.items) |p| {
|
||||
if (std.mem.eql(u8, p.alias, name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn contains(scope: *Block, name: []const u8) bool {
|
||||
if (scope.localContains(name))
|
||||
return true;
|
||||
return scope.base.parent.?.contains(name);
|
||||
}
|
||||
|
||||
pub fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
|
||||
const name_node = try Tag.identifier.create(c.arena, name);
|
||||
const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
|
||||
try scope.statements.append(discard);
|
||||
try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Root = struct {
|
||||
base: Scope,
|
||||
sym_table: SymbolTable,
|
||||
macro_table: SymbolTable,
|
||||
blank_macros: std.StringArrayHashMap(void),
|
||||
context: *Context,
|
||||
nodes: std.ArrayList(Node),
|
||||
|
||||
pub fn init(c: *Context) Root {
|
||||
return .{
|
||||
.base = .{
|
||||
.id = .root,
|
||||
.parent = null,
|
||||
},
|
||||
.sym_table = SymbolTable.init(c.gpa),
|
||||
.macro_table = SymbolTable.init(c.gpa),
|
||||
.blank_macros = std.StringArrayHashMap(void).init(c.gpa),
|
||||
.context = c,
|
||||
.nodes = std.ArrayList(Node).init(c.gpa),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(scope: *Root) void {
|
||||
scope.sym_table.deinit();
|
||||
scope.macro_table.deinit();
|
||||
scope.blank_macros.deinit();
|
||||
scope.nodes.deinit();
|
||||
}
|
||||
|
||||
/// Check if the global scope contains this name, without looking into the "future", e.g.
|
||||
/// ignore the preprocessed decl and macro names.
|
||||
pub fn containsNow(scope: *Root, name: []const u8) bool {
|
||||
return scope.sym_table.contains(name) or scope.macro_table.contains(name);
|
||||
}
|
||||
|
||||
/// Check if the global scope contains the name, includes all decls that haven't been translated yet.
|
||||
pub fn contains(scope: *Root, name: []const u8) bool {
|
||||
return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.root => unreachable,
|
||||
.block => return @fieldParentPtr(Block, "base", scope),
|
||||
.condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn findBlockReturnType(inner: *Scope) Type {
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.root => unreachable,
|
||||
.block => {
|
||||
const block = @fieldParentPtr(Block, "base", scope);
|
||||
if (block.return_type) |ty| return ty;
|
||||
scope = scope.parent.?;
|
||||
},
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getAlias(scope: *Scope, name: []const u8) []const u8 {
|
||||
return switch (scope.id) {
|
||||
.root => return name,
|
||||
.block => @fieldParentPtr(Block, "base", scope).getAlias(name),
|
||||
.loop, .do_loop, .condition => scope.parent.?.getAlias(name),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn contains(scope: *Scope, name: []const u8) bool {
|
||||
return switch (scope.id) {
|
||||
.root => @fieldParentPtr(Root, "base", scope).contains(name),
|
||||
.block => @fieldParentPtr(Block, "base", scope).contains(name),
|
||||
.loop, .do_loop, .condition => scope.parent.?.contains(name),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getBreakableScope(inner: *Scope) *Scope {
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.root => unreachable,
|
||||
.loop, .do_loop => return scope,
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Appends a node to the first block scope if inside a function, or to the root tree if not.
|
||||
pub fn appendNode(inner: *Scope, node: Node) !void {
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.root => {
|
||||
const root = @fieldParentPtr(Root, "base", scope);
|
||||
return root.nodes.append(node);
|
||||
},
|
||||
.block => {
|
||||
const block = @fieldParentPtr(Block, "base", scope);
|
||||
return block.statements.append(node);
|
||||
},
|
||||
else => scope = scope.parent.?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
|
||||
if (true) {
|
||||
// TODO: due to 'local variable is never mutated' errors, we can
|
||||
// only skip discards if a variable is used as an lvalue, which
|
||||
// we don't currently have detection for in translate-c.
|
||||
// Once #17584 is completed, perhaps we can do away with this
|
||||
// logic entirely, and instead rely on render to fixup code.
|
||||
return;
|
||||
}
|
||||
var scope = inner;
|
||||
while (true) {
|
||||
switch (scope.id) {
|
||||
.root => return,
|
||||
.block => {
|
||||
const block = @fieldParentPtr(Block, "base", scope);
|
||||
if (block.variable_discards.get(name)) |discard| {
|
||||
discard.data.should_skip = true;
|
||||
return;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
scope = scope.parent.?;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue