Merge pull request #25780 from Vexu/translate-c

Update Aro and translate-c to latest
This commit is contained in:
Andrew Kelley 2025-11-20 10:24:31 -08:00 committed by GitHub
commit db622f14c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 47635 additions and 16585 deletions

View File

@ -6,7 +6,7 @@ pub const Parser = @import("aro/Parser.zig");
pub const Preprocessor = @import("aro/Preprocessor.zig");
pub const Source = @import("aro/Source.zig");
pub const StringInterner = @import("aro/StringInterner.zig");
pub const target_util = @import("aro/target.zig");
pub const Target = @import("aro/Target.zig");
pub const Tokenizer = @import("aro/Tokenizer.zig");
pub const Toolchain = @import("aro/Toolchain.zig");
pub const Tree = @import("aro/Tree.zig");
@ -31,11 +31,11 @@ test {
_ = @import("aro/char_info.zig");
_ = @import("aro/Compilation.zig");
_ = @import("aro/Driver/Distro.zig");
_ = @import("aro/Driver/Filesystem.zig");
_ = @import("aro/Driver/GCCVersion.zig");
_ = @import("aro/InitList.zig");
_ = @import("aro/LangOpts.zig");
_ = @import("aro/Preprocessor.zig");
_ = @import("aro/target.zig");
_ = @import("aro/Target.zig");
_ = @import("aro/Tokenizer.zig");
_ = @import("aro/Value.zig");
}

View File

@ -61,25 +61,21 @@ pub const Iterator = struct {
return .{ self.slice[self.index], self.index };
}
if (self.source) |*source| {
var cur = source.qt;
if (cur.isInvalid()) {
if (source.qt.isInvalid()) {
self.source = null;
return null;
}
while (true) switch (cur.type(source.comp)) {
.typeof => |typeof| cur = typeof.base,
loop: switch (source.qt.type(source.comp)) {
.typeof => |typeof| continue :loop typeof.base.type(source.comp),
.attributed => |attributed| {
self.slice = attributed.attributes;
self.index = 1;
source.qt = attributed.base;
return .{ self.slice[0], 0 };
},
.typedef => |typedef| cur = typedef.base,
else => {
self.source = null;
break;
},
};
.typedef => |typedef| continue :loop typedef.base.type(source.comp),
else => self.source = null,
}
}
return null;
}
@ -712,6 +708,9 @@ const attributes = struct {
pub const thiscall = struct {};
pub const sysv_abi = struct {};
pub const ms_abi = struct {};
// TODO cannot be combined with weak or selectany
pub const internal_linkage = struct {};
pub const availability = struct {};
};
pub const Tag = std.meta.DeclEnum(attributes);
@ -776,9 +775,9 @@ pub fn fromString(kind: Kind, namespace: ?[]const u8, name: []const u8) ?Tag {
const tag_and_opts = attribute_names.fromName(normalized) orelse return null;
switch (actual_kind) {
inline else => |tag| {
if (@field(tag_and_opts.properties, @tagName(tag)))
return tag_and_opts.properties.tag;
inline else => |available_kind| {
if (@field(tag_and_opts, @tagName(available_kind)))
return tag_and_opts.tag;
},
}
return null;
@ -814,7 +813,7 @@ fn applyVariableOrParameterAttributes(p: *Parser, qt: QualType, attr_buf_start:
for (attrs, toks) |attr, tok| switch (attr.tag) {
// zig fmt: off
.alias, .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .weak, .used,
.noinit, .retain, .persistent, .section, .mode, .asm_label, .nullability, .unaligned,
.noinit, .retain, .persistent, .section, .mode, .asm_label, .nullability, .unaligned, .selectany, .internal_linkage,
=> try p.attr_application_buf.append(gpa, attr),
// zig fmt: on
.common => if (nocommon) {
@ -874,18 +873,18 @@ fn applyVariableOrParameterAttributes(p: *Parser, qt: QualType, attr_buf_start:
pub fn applyFieldAttributes(p: *Parser, field_qt: *QualType, attr_buf_start: usize) ![]const Attribute {
const attrs = p.attr_buf.items(.attr)[attr_buf_start..];
const toks = p.attr_buf.items(.tok)[attr_buf_start..];
const seen = p.attr_buf.items(.seen)[attr_buf_start..];
p.attr_application_buf.items.len = 0;
for (attrs, toks) |attr, tok| switch (attr.tag) {
// zig fmt: off
.@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned,
.mode, .warn_unused_result, .nodiscard, .nullability, .unaligned,
=> try p.attr_application_buf.append(p.comp.gpa, attr),
// zig fmt: on
.vector_size => try attr.applyVectorSize(p, tok, field_qt),
.aligned => try attr.applyAligned(p, field_qt.*, null),
.calling_convention => try applyCallingConvention(attr, p, tok, field_qt.*),
else => try ignoredAttrErr(p, tok, attr.tag, "fields"),
for (attrs, 0..) |attr, i| switch (attr.tag) {
.@"packed" => {
try p.attr_application_buf.append(p.comp.gpa, attr);
seen[i] = true;
},
.aligned => {
try attr.applyAligned(p, field_qt.*, null);
seen[i] = true;
},
else => {},
};
return p.attr_application_buf.items;
}
@ -894,11 +893,16 @@ pub fn applyTypeAttributes(p: *Parser, qt: QualType, attr_buf_start: usize, diag
const gpa = p.comp.gpa;
const attrs = p.attr_buf.items(.attr)[attr_buf_start..];
const toks = p.attr_buf.items(.tok)[attr_buf_start..];
const seens = p.attr_buf.items(.seen)[attr_buf_start..];
p.attr_application_buf.items.len = 0;
var base_qt = qt;
for (attrs, toks) |attr, tok| switch (attr.tag) {
for (attrs, toks, seens) |attr, tok, seen| {
if (seen) continue;
switch (attr.tag) {
// zig fmt: off
.@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode, .nullability, .unaligned,
.@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode,
.nullability, .unaligned, .warn_unused_result,
=> try p.attr_application_buf.append(gpa, attr),
// zig fmt: on
.transparent_union => try attr.applyTransparentUnion(p, tok, base_qt),
@ -916,7 +920,8 @@ pub fn applyTypeAttributes(p: *Parser, qt: QualType, attr_buf_start: usize, diag
.nonstring,
=> |t| try p.err(tok, .attribute_todo, .{ @tagName(t), "types" }),
else => try ignoredAttrErr(p, tok, attr.tag, "types"),
};
}
}
return applySelected(base_qt, p);
}
@ -935,7 +940,7 @@ pub fn applyFunctionAttributes(p: *Parser, qt: QualType, attr_buf_start: usize)
.noreturn, .unused, .used, .warning, .deprecated, .unavailable, .weak, .pure, .leaf,
.@"const", .warn_unused_result, .section, .returns_nonnull, .returns_twice, .@"error",
.externally_visible, .retain, .flatten, .gnu_inline, .alias, .asm_label, .nodiscard,
.reproducible, .unsequenced, .nothrow, .nullability, .unaligned,
.reproducible, .unsequenced, .nothrow, .nullability, .unaligned, .internal_linkage,
=> try p.attr_application_buf.append(gpa, attr),
// zig fmt: on
.hot => if (cold) {
@ -1164,7 +1169,7 @@ pub fn applyStatementAttributes(p: *Parser, expr_start: TokenIndex, attr_buf_sta
try p.attr_application_buf.append(p.comp.gpa, attr);
break;
},
.r_brace => {},
.r_brace, .semicolon => {},
else => {
try p.err(expr_start, .invalid_fallthrough, .{});
break;

File diff suppressed because it is too large Load Diff

View File

@ -3,25 +3,75 @@ const std = @import("std");
const Compilation = @import("Compilation.zig");
const LangOpts = @import("LangOpts.zig");
const Parser = @import("Parser.zig");
const target_util = @import("target.zig");
const Target = @import("Target.zig");
const TypeStore = @import("TypeStore.zig");
const QualType = TypeStore.QualType;
const Builder = TypeStore.Builder;
const TypeDescription = @import("Builtins/TypeDescription.zig");
const properties = @import("Builtins/properties.zig");
const Properties = @import("Builtins/Properties.zig");
pub const Builtin = @import("Builtins/Builtin.zig").with(Properties);
const Expanded = struct {
qt: QualType,
builtin: Builtin,
const BuiltinBase = struct {
param_str: [*:0]const u8,
language: properties.Language = .all_languages,
attributes: properties.Attributes = .{},
header: properties.Header = .none,
};
const NameToTypeMap = std.StringHashMapUnmanaged(QualType);
const BuiltinTarget = struct {
param_str: [*:0]const u8,
language: properties.Language = .all_languages,
attributes: properties.Attributes = .{},
header: properties.Header = .none,
features: ?[*:0]const u8 = null,
};
const aarch64 = @import("Builtins/aarch64.zig").with(BuiltinTarget);
const amdgcn = @import("Builtins/amdgcn.zig").with(BuiltinTarget);
const arm = @import("Builtins/arm.zig").with(BuiltinTarget);
const bpf = @import("Builtins/bpf.zig").with(BuiltinTarget);
const common = @import("Builtins/common.zig").with(BuiltinBase);
const hexagon = @import("Builtins/hexagon.zig").with(BuiltinTarget);
const loongarch = @import("Builtins/loongarch.zig").with(BuiltinTarget);
const mips = @import("Builtins/mips.zig").with(BuiltinBase);
const nvptx = @import("Builtins/nvptx.zig").with(BuiltinTarget);
const powerpc = @import("Builtins/powerpc.zig").with(BuiltinTarget);
const riscv = @import("Builtins/riscv.zig").with(BuiltinTarget);
const s390x = @import("Builtins/s390x.zig").with(BuiltinTarget);
const ve = @import("Builtins/ve.zig").with(BuiltinBase);
const x86_64 = @import("Builtins/x86_64.zig").with(BuiltinTarget);
const x86 = @import("Builtins/x86.zig").with(BuiltinTarget);
const xcore = @import("Builtins/xcore.zig").with(BuiltinBase);
pub const Tag = union(enum) {
aarch64: aarch64.Tag,
amdgcn: amdgcn.Tag,
arm: arm.Tag,
bpf: bpf.Tag,
common: common.Tag,
hexagon: hexagon.Tag,
loongarch: loongarch.Tag,
mips: mips.Tag,
nvptx: nvptx.Tag,
powerpc: powerpc.Tag,
riscv: riscv.Tag,
s390x: s390x.Tag,
ve: ve.Tag,
x86_64: x86_64.Tag,
x86: x86.Tag,
xcore: xcore.Tag,
};
pub const Expanded = struct {
tag: Tag,
qt: QualType,
language: properties.Language = .all_languages,
attributes: properties.Attributes = .{},
header: properties.Header = .none,
};
const Builtins = @This();
_name_to_type_map: NameToTypeMap = .{},
_name_to_type_map: std.StringHashMapUnmanaged(Expanded) = .{},
pub fn deinit(b: *Builtins, gpa: std.mem.Allocator) void {
b._name_to_type_map.deinit(gpa);
@ -47,6 +97,7 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
var parser: Parser = undefined;
parser.comp = comp;
var builder: TypeStore.Builder = .{ .parser = &parser, .error_on_invalid = true };
var actual_suffix = desc.suffix;
var require_native_int32 = false;
var require_native_int64 = false;
@ -66,7 +117,7 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
.W => require_native_int64 = true,
.N => {
std.debug.assert(desc.spec == .i);
if (!target_util.isLP64(comp.target)) {
if (!comp.target.isLP64()) {
builder.combine(.long, 0) catch unreachable;
}
},
@ -102,10 +153,7 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
},
.h => builder.combine(.fp16, 0) catch unreachable,
.x => builder.combine(.float16, 0) catch unreachable,
.y => {
// Todo: __bf16
return .invalid;
},
.y => builder.combine(.bf16, 0) catch unreachable,
.f => builder.combine(.float, 0) catch unreachable,
.d => {
if (builder.type == .long_long) {
@ -126,18 +174,6 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
std.debug.assert(builder.type == .none);
builder.type = Builder.fromType(comp, comp.type_store.ns_constant_string);
},
.G => {
// Todo: id
return .invalid;
},
.H => {
// Todo: SEL
return .invalid;
},
.M => {
// Todo: struct objc_super
return .invalid;
},
.a => {
std.debug.assert(builder.type == .none);
std.debug.assert(desc.suffix.len == 0);
@ -152,7 +188,9 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
},
.V => |element_count| {
std.debug.assert(desc.suffix.len == 0);
const child_desc = it.next().?;
var child_desc = it.next().?;
actual_suffix = child_desc.suffix;
child_desc.suffix = &.{};
const elem_qt = try createType(child_desc, undefined, comp);
const vector_qt = try comp.type_store.put(comp.gpa, .{ .vector = .{
.elem = elem_qt,
@ -160,8 +198,8 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
} });
builder.type = .{ .other = vector_qt };
},
.q => {
// Todo: scalable vector
.Q => {
// Todo: target builtin type
return .invalid;
},
.E => {
@ -219,9 +257,8 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
std.debug.assert(desc.suffix.len == 0);
builder.type = Builder.fromType(comp, comp.type_store.pid_t);
},
.@"!" => return .invalid,
}
for (desc.suffix) |suffix| {
for (actual_suffix) |suffix| {
switch (suffix) {
.@"*" => |address_space| {
_ = address_space; // TODO: handle address space
@ -243,144 +280,122 @@ fn createType(desc: TypeDescription, it: *TypeDescription.TypeIterator, comp: *C
return builder.finish() catch unreachable;
}
fn createBuiltin(comp: *Compilation, builtin: Builtin) !QualType {
var it = TypeDescription.TypeIterator.init(builtin.properties.param_str);
fn createBuiltin(comp: *Compilation, param_str: [*:0]const u8) !QualType {
var it = TypeDescription.TypeIterator.init(param_str);
const ret_ty_desc = it.next().?;
if (ret_ty_desc.spec == .@"!") {
// Todo: handle target-dependent definition
}
const ret_ty = try createType(ret_ty_desc, &it, comp);
var param_count: usize = 0;
var params: [Builtin.max_param_count]TypeStore.Type.Func.Param = undefined;
var params: [32]TypeStore.Type.Func.Param = undefined;
while (it.next()) |desc| : (param_count += 1) {
params[param_count] = .{ .name_tok = 0, .qt = try createType(desc, &it, comp), .name = .empty, .node = .null };
}
return comp.type_store.put(comp.gpa, .{ .func = .{
.return_type = ret_ty,
.kind = if (builtin.properties.isVarArgs()) .variadic else .normal,
.kind = if (properties.isVarArgs(param_str)) .variadic else .normal,
.params = params[0..param_count],
} });
}
/// Asserts that the builtin has already been created
pub fn lookup(b: *const Builtins, name: []const u8) Expanded {
const builtin = Builtin.fromName(name).?;
const qt = b._name_to_type_map.get(name).?;
return .{ .builtin = builtin, .qt = qt };
return b._name_to_type_map.get(name).?;
}
pub fn getOrCreate(b: *Builtins, comp: *Compilation, name: []const u8) !?Expanded {
const qt = b._name_to_type_map.get(name) orelse {
const builtin = Builtin.fromName(name) orelse return null;
if (!comp.hasBuiltinFunction(builtin)) return null;
if (b._name_to_type_map.get(name)) |expanded| return expanded;
const builtin = fromName(comp, name) orelse return null;
if (builtin.features) |_| {
// TODO check features
}
try b._name_to_type_map.ensureUnusedCapacity(comp.gpa, 1);
const qt = try createBuiltin(comp, builtin);
b._name_to_type_map.putAssumeCapacity(name, qt);
return .{
.builtin = builtin,
.qt = qt,
const expanded: Expanded = .{
.tag = builtin.tag,
.qt = try createBuiltin(comp, builtin.param_str),
.attributes = builtin.attributes,
.header = builtin.header,
.language = builtin.language,
};
};
const builtin = Builtin.fromName(name).?;
return .{ .builtin = builtin, .qt = qt };
b._name_to_type_map.putAssumeCapacity(name, expanded);
return expanded;
}
pub const Iterator = struct {
index: u16 = 1,
name_buf: [Builtin.longest_name]u8 = undefined,
pub const Entry = struct {
/// Memory of this slice is overwritten on every call to `next`
name: []const u8,
builtin: Builtin,
};
pub fn next(self: *Iterator) ?Entry {
if (self.index > Builtin.data.len) return null;
const index = self.index;
const data_index = index - 1;
self.index += 1;
return .{
.name = Builtin.nameFromUniqueIndex(index, &self.name_buf),
.builtin = Builtin.data[data_index],
};
}
pub const FromName = struct {
tag: Tag,
param_str: [*:0]const u8,
language: properties.Language = .all_languages,
attributes: properties.Attributes = .{},
header: properties.Header = .none,
features: ?[*:0]const u8 = null,
};
test Iterator {
const gpa = std.testing.allocator;
var it = Iterator{};
var seen: std.StringHashMapUnmanaged(Builtin) = .empty;
defer seen.deinit(gpa);
var arena_state = std.heap.ArenaAllocator.init(gpa);
defer arena_state.deinit();
const arena = arena_state.allocator();
while (it.next()) |entry| {
const index = Builtin.uniqueIndex(entry.name).?;
var buf: [Builtin.longest_name]u8 = undefined;
const name_from_index = Builtin.nameFromUniqueIndex(index, &buf);
try std.testing.expectEqualStrings(entry.name, name_from_index);
if (seen.contains(entry.name)) {
std.debug.print("iterated over {s} twice\n", .{entry.name});
std.debug.print("current data: {}\n", .{entry.builtin});
std.debug.print("previous data: {}\n", .{seen.get(entry.name).?});
return error.TestExpectedUniqueEntries;
pub fn fromName(comp: *Compilation, name: []const u8) ?FromName {
if (fromNameExtra(name, .common)) |found| return found;
switch (comp.target.cpu.arch) {
.aarch64, .aarch64_be => if (fromNameExtra(name, .aarch64)) |found| return found,
.amdgcn => if (fromNameExtra(name, .amdgcn)) |found| return found,
.arm, .armeb, .thumb, .thumbeb => if (fromNameExtra(name, .arm)) |found| return found,
.bpfeb, .bpfel => if (fromNameExtra(name, .bpf)) |found| return found,
.hexagon => if (fromNameExtra(name, .hexagon)) |found| return found,
.loongarch32, .loongarch64 => if (fromNameExtra(name, .loongarch)) |found| return found,
.mips64, .mips64el, .mips, .mipsel => if (fromNameExtra(name, .mips)) |found| return found,
.nvptx, .nvptx64 => if (fromNameExtra(name, .nvptx)) |found| return found,
.powerpc64, .powerpc64le, .powerpc, .powerpcle => if (fromNameExtra(name, .powerpc)) |found| return found,
.riscv32, .riscv32be, .riscv64, .riscv64be => if (fromNameExtra(name, .riscv)) |found| return found,
.s390x => if (fromNameExtra(name, .s390x)) |found| return found,
.ve => if (fromNameExtra(name, .ve)) |found| return found,
.xcore => if (fromNameExtra(name, .xcore)) |found| return found,
.x86_64 => {
if (fromNameExtra(name, .x86_64)) |found| return found;
if (fromNameExtra(name, .x86)) |found| return found;
},
.x86 => if (fromNameExtra(name, .x86)) |found| return found,
else => {},
}
try seen.put(gpa, try arena.dupe(u8, entry.name), entry.builtin);
}
try std.testing.expectEqual(@as(usize, Builtin.data.len), seen.count());
return null;
}
test "All builtins" {
var arena_state: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
fn fromNameExtra(name: []const u8, comptime arch: std.meta.Tag(Tag)) ?FromName {
const list = @field(@This(), @tagName(arch));
const tag = list.tagFromName(name) orelse return null;
const builtin = list.data[@intFromEnum(tag)];
var comp = Compilation.init(std.testing.allocator, arena, undefined, std.fs.cwd());
defer comp.deinit();
try comp.type_store.initNamedTypes(&comp);
comp.type_store.va_list = try comp.type_store.va_list.decay(&comp);
var builtin_it = Iterator{};
while (builtin_it.next()) |entry| {
const name = try arena.dupe(u8, entry.name);
if (try comp.builtins.getOrCreate(&comp, name)) |func_ty| {
const get_again = (try comp.builtins.getOrCreate(&comp, name)).?;
const found_by_lookup = comp.builtins.lookup(name);
try std.testing.expectEqual(func_ty.builtin.tag, get_again.builtin.tag);
try std.testing.expectEqual(func_ty.builtin.tag, found_by_lookup.builtin.tag);
}
}
}
test "Allocation failures" {
const Test = struct {
fn testOne(allocator: std.mem.Allocator) !void {
var arena_state: std.heap.ArenaAllocator = .init(allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
var comp = Compilation.init(allocator, arena, undefined, std.fs.cwd());
defer comp.deinit();
_ = try comp.generateBuiltinMacros(.include_system_defines);
const num_builtins = 40;
var builtin_it = Iterator{};
for (0..num_builtins) |_| {
const entry = builtin_it.next().?;
_ = try comp.builtins.getOrCreate(&comp, entry.name);
}
}
return .{
.tag = @unionInit(Tag, @tagName(arch), tag),
.param_str = builtin.param_str,
.header = builtin.header,
.language = builtin.language,
.attributes = builtin.attributes,
.features = if (@hasField(@TypeOf(builtin), "features")) builtin.features else null,
};
try std.testing.checkAllAllocationFailures(std.testing.allocator, Test.testOne, .{});
}
test "all builtins" {
const list_names = comptime std.meta.fieldNames(Tag);
inline for (list_names) |list_name| {
const list = @field(Builtins, list_name);
for (list.data, 0..) |builtin, index| {
{
var it = TypeDescription.TypeIterator.init(builtin.param_str);
while (it.next()) |_| {}
}
if (@hasField(@TypeOf(builtin), "features")) {
const corrected_name = comptime if (std.mem.eql(u8, list_name, "x86_64")) "x86" else list_name;
const features = &@field(std.Target, corrected_name).all_features;
const feature_string = builtin.features orelse continue;
var it = std.mem.tokenizeAny(u8, std.mem.span(feature_string), "()|,");
outer: while (it.next()) |feature| {
for (features) |valid_feature| {
if (std.mem.eql(u8, feature, valid_feature.name)) continue :outer;
}
std.debug.panic("unknown feature {s} on {t}\n", .{ feature, @as(list.Tag, @enumFromInt(index)) });
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,10 @@ pub const Component = union(enum) {
};
pub const ComponentIterator = struct {
str: []const u8,
str: [*:0]const u8,
idx: usize,
pub fn init(str: []const u8) ComponentIterator {
pub fn init(str: [*:0]const u8) ComponentIterator {
return .{
.str = str,
.idx = 0,
@ -30,8 +30,8 @@ pub const ComponentIterator = struct {
}
pub fn next(self: *ComponentIterator) ?Component {
if (self.idx == self.str.len) return null;
const c = self.str[self.idx];
if (c == 0) return null;
self.idx += 1;
switch (c) {
'L' => {
@ -68,18 +68,14 @@ pub const ComponentIterator = struct {
'z' => return .{ .spec = .z },
'w' => return .{ .spec = .w },
'F' => return .{ .spec = .F },
'G' => return .{ .spec = .G },
'H' => return .{ .spec = .H },
'M' => return .{ .spec = .M },
'a' => return .{ .spec = .a },
'A' => return .{ .spec = .A },
'V', 'q', 'E' => {
'V', 'E' => {
const start = self.idx;
while (std.ascii.isDigit(self.str[self.idx])) : (self.idx += 1) {}
const count = std.fmt.parseUnsigned(u32, self.str[start..self.idx], 10) catch unreachable;
return switch (c) {
'V' => .{ .spec = .{ .V = count } },
'q' => .{ .spec = .{ .q = count } },
'E' => .{ .spec = .{ .E = count } },
else => unreachable,
};
@ -103,16 +99,12 @@ pub const ComponentIterator = struct {
'p' => return .{ .spec = .p },
'.' => {
// can only appear at end of param string; indicates varargs function
std.debug.assert(self.idx == self.str.len);
std.debug.assert(self.str[self.idx] == 0);
return null;
},
'!' => {
std.debug.assert(self.str.len == 1);
return .{ .spec = .@"!" };
},
'*' => {
if (self.idx < self.str.len and std.ascii.isDigit(self.str[self.idx])) {
if (std.ascii.isDigit(self.str[self.idx])) {
defer self.idx += 1;
const addr_space = self.str[self.idx] - '0';
return .{ .suffix = .{ .@"*" = addr_space } };
@ -123,6 +115,14 @@ pub const ComponentIterator = struct {
'C' => return .{ .suffix = .C },
'D' => return .{ .suffix = .D },
'R' => return .{ .suffix = .R },
'Q' => {
defer self.idx += 1;
switch (self.str[self.idx]) {
'a' => return .{ .spec = .{ .Q = .aarch64_svcount_t } },
'b' => return .{ .spec = .{ .Q = .amdgpu_buffer_rsrc_t } },
else => unreachable,
}
},
else => unreachable,
}
return null;
@ -130,13 +130,13 @@ pub const ComponentIterator = struct {
};
pub const TypeIterator = struct {
param_str: []const u8,
param_str: [*:0]const u8,
prefix: [4]Prefix,
spec: Spec,
suffix: [4]Suffix,
idx: usize,
pub fn init(param_str: []const u8) TypeIterator {
pub fn init(param_str: [*:0]const u8) TypeIterator {
return .{
.param_str = param_str,
.prefix = undefined,
@ -176,7 +176,7 @@ pub const TypeIterator = struct {
_ = it.next();
}
if (maybe_spec) |spec| {
return TypeDescription{
return .{
.prefix = self.prefix[0..prefix_count],
.spec = spec,
.suffix = self.suffix[0..suffix_count],
@ -236,20 +236,17 @@ const Spec = union(enum) {
w,
/// constant CFString
F,
/// id
G,
/// SEL
H,
/// struct objc_super
M,
/// __builtin_va_list
a,
/// "reference" to __builtin_va_list
A,
/// Vector, followed by the number of elements and the base type.
V: u32,
/// Scalable vector, followed by the number of elements and the base type.
q: u32,
/// target builtin type, followed by a character to distinguish the builtin type
Q: enum {
aarch64_svcount_t,
amdgpu_buffer_rsrc_t,
},
/// ext_vector, followed by the number of elements and the base type.
E: u32,
/// _Complex, followed by the base type.
@ -270,8 +267,6 @@ const Spec = union(enum) {
K,
/// pid_t
p,
/// Used to indicate a builtin with target-dependent param types. Must appear by itself
@"!",
};
const Suffix = union(enum) {

1150
lib/compiler/aro/aro/Builtins/aarch64.zig vendored Normal file

File diff suppressed because it is too large Load Diff

2851
lib/compiler/aro/aro/Builtins/amdgcn.zig vendored Normal file

File diff suppressed because it is too large Load Diff

1076
lib/compiler/aro/aro/Builtins/arm.zig vendored Normal file

File diff suppressed because it is too large Load Diff

231
lib/compiler/aro/aro/Builtins/bpf.zig vendored Normal file
View File

@ -0,0 +1,231 @@
//! Autogenerated by GenerateDef from src/aro/Builtins/bpf.def, do not edit
// zig fmt: off
const std = @import("std");
pub fn with(comptime Properties: type) type {
return struct {
/// Integer starting at 0 derived from the unique index,
/// corresponds with the data array index.
pub const Tag = enum(u16) { __builtin_btf_type_id,
__builtin_preserve_enum_value,
__builtin_preserve_field_info,
__builtin_preserve_type_info,
};
pub fn fromName(name: []const u8) ?Properties {
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) Properties {
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;
}
pub const shortest_name = 21;
pub const longest_name = 29;
/// 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 {
@setEvalBranchQuota(8);
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 w = std.Io.Writer.fixed(buf);
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 w.buffered();
}
const Node = packed struct {
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: std.math.IntFittingRange(0, data.len),
/// 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,
/// Index of the first child of this node.
child_index: u16,
};
const dafsa = [_]Node{
.{ .char = 0, .end_of_word = false, .end_of_list = true, .number = 0, .child_index = 1 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 2 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 3 },
.{ .char = 'b', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 4 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 5 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 6 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 7 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 8 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 9 },
.{ .char = 'n', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 10 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 11 },
.{ .char = 'b', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 13 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 14 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 15 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 16 },
.{ .char = 'f', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 17 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 18 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 19 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 20 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 21 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 22 },
.{ .char = 'y', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 23 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 24 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 25 },
.{ .char = 'v', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 26 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 27 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 28 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 29 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 3, .child_index = 30 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 33 },
.{ .char = 'e', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 34 },
.{ .char = 'f', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 35 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 36 },
.{ .char = 'd', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'n', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 37 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 38 },
.{ .char = 'y', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 39 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 40 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 41 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 42 },
.{ .char = 'm', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 43 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 44 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 45 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 46 },
.{ .char = 'd', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 45 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 47 },
.{ .char = 'v', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 48 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 49 },
.{ .char = 'a', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 50 },
.{ .char = 'n', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 51 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 52 },
.{ .char = 'f', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 53 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 54 },
.{ .char = 'o', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'e', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
};
pub const data = blk: {
@setEvalBranchQuota(36);
break :blk [_]Properties{
.{ .param_str = "LUi.", .attributes = .{ .custom_typecheck = true } },
.{ .param_str = "Li.", .attributes = .{ .custom_typecheck = true } },
.{ .param_str = "Ui.", .attributes = .{ .custom_typecheck = true } },
.{ .param_str = "LUi.", .attributes = .{ .custom_typecheck = true } },
};
};
};
}

5527
lib/compiler/aro/aro/Builtins/common.zig vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@ const std = @import("std");
const backend = @import("../../backend.zig");
const Interner = backend.Interner;
const Builtins = @import("../Builtins.zig");
const Builtin = Builtins.Builtin;
const Parser = @import("../Parser.zig");
const Tree = @import("../Tree.zig");
const TypeStore = @import("../TypeStore.zig");
@ -23,11 +22,11 @@ fn makeNan(comptime T: type, str: []const u8) T {
return @bitCast(@as(UnsignedSameSize, bits) | @as(UnsignedSameSize, @bitCast(std.math.nan(T))));
}
pub fn eval(tag: Builtin.Tag, p: *Parser, args: []const Tree.Node.Index) !Value {
const builtin = Builtin.fromTag(tag);
if (!builtin.properties.attributes.const_evaluable) return .{};
pub fn eval(expanded: Builtins.Expanded, p: *Parser, args: []const Tree.Node.Index) !Value {
if (!expanded.attributes.const_evaluable) return .{};
switch (tag) {
switch (expanded.tag) {
.common => |tag| switch (tag) {
.__builtin_inff,
.__builtin_inf,
.__builtin_infl,
@ -82,6 +81,8 @@ pub fn eval(tag: Builtin.Tag, p: *Parser, args: []const Tree.Node.Index) !Value
return Value.intern(p.comp, .{ .float = f });
},
else => {},
},
else => {},
}
return .{};
}

6500
lib/compiler/aro/aro/Builtins/hexagon.zig vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2110
lib/compiler/aro/aro/Builtins/mips.zig vendored Normal file

File diff suppressed because it is too large Load Diff

3253
lib/compiler/aro/aro/Builtins/nvptx.zig vendored Normal file

File diff suppressed because it is too large Load Diff

2491
lib/compiler/aro/aro/Builtins/powerpc.zig vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,5 @@
const std = @import("std");
const Properties = @This();
param_str: []const u8,
language: Language = .all_languages,
attributes: Attributes = Attributes{},
header: Header = .none,
target_set: TargetSet = TargetSet.initOne(.basic),
/// Header which must be included for a builtin to be available
pub const Header = enum {
none,
@ -41,6 +33,18 @@ pub const Header = enum {
complex,
/// Blocks.h
blocks,
/// intrin.h
intrin,
/// immintrin.h
immintrin,
/// xmmintrin.h
xmmintrin,
/// emmintrin.h
emmintrin,
/// mmintrin.h
mmintrin,
/// arm_acle.h
arm_acle,
};
/// Languages in which a builtin is available
@ -49,6 +53,7 @@ pub const Language = enum {
all_ms_languages,
all_gnu_languages,
gnu_lang,
c23_lang,
};
pub const Attributes = packed struct {
@ -106,38 +111,7 @@ pub const Attributes = packed struct {
const_evaluable: bool = false,
};
pub const Target = enum {
/// Supported on all targets
basic,
aarch64,
aarch64_neon_sve_bridge,
aarch64_neon_sve_bridge_cg,
amdgpu,
arm,
bpf,
hexagon,
hexagon_dep,
hexagon_map_custom_dep,
loong_arch,
mips,
neon,
nvptx,
ppc,
riscv,
riscv_vector,
sve,
systemz,
ve,
vevl_gen,
webassembly,
x86,
x86_64,
xcore,
};
/// Targets for which a builtin is enabled
pub const TargetSet = std.enums.EnumSet(Target);
pub fn isVarArgs(properties: Properties) bool {
return properties.param_str[properties.param_str.len - 1] == '.';
pub fn isVarArgs(param_str: [*:0]const u8) bool {
const slice = std.mem.sliceTo(param_str, 0);
return slice[slice.len - 1] == '.';
}

469
lib/compiler/aro/aro/Builtins/riscv.zig vendored Normal file
View File

@ -0,0 +1,469 @@
//! Autogenerated by GenerateDef from src/aro/Builtins/riscv.def, do not edit
// zig fmt: off
const std = @import("std");
pub fn with(comptime Properties: type) type {
return struct {
/// Integer starting at 0 derived from the unique index,
/// corresponds with the data array index.
pub const Tag = enum(u16) { __builtin_riscv_aes32dsi,
__builtin_riscv_aes32dsmi,
__builtin_riscv_aes32esi,
__builtin_riscv_aes32esmi,
__builtin_riscv_aes64ds,
__builtin_riscv_aes64dsm,
__builtin_riscv_aes64es,
__builtin_riscv_aes64esm,
__builtin_riscv_aes64im,
__builtin_riscv_aes64ks1i,
__builtin_riscv_aes64ks2,
__builtin_riscv_brev8_32,
__builtin_riscv_brev8_64,
__builtin_riscv_clmul_32,
__builtin_riscv_clmul_64,
__builtin_riscv_clmulh_32,
__builtin_riscv_clmulh_64,
__builtin_riscv_clmulr_32,
__builtin_riscv_clmulr_64,
__builtin_riscv_clz_32,
__builtin_riscv_clz_64,
__builtin_riscv_ctz_32,
__builtin_riscv_ctz_64,
__builtin_riscv_cv_alu_addN,
__builtin_riscv_cv_alu_addRN,
__builtin_riscv_cv_alu_adduN,
__builtin_riscv_cv_alu_adduRN,
__builtin_riscv_cv_alu_clip,
__builtin_riscv_cv_alu_clipu,
__builtin_riscv_cv_alu_extbs,
__builtin_riscv_cv_alu_extbz,
__builtin_riscv_cv_alu_exths,
__builtin_riscv_cv_alu_exthz,
__builtin_riscv_cv_alu_sle,
__builtin_riscv_cv_alu_sleu,
__builtin_riscv_cv_alu_subN,
__builtin_riscv_cv_alu_subRN,
__builtin_riscv_cv_alu_subuN,
__builtin_riscv_cv_alu_subuRN,
__builtin_riscv_ntl_load,
__builtin_riscv_ntl_store,
__builtin_riscv_orc_b_32,
__builtin_riscv_orc_b_64,
__builtin_riscv_pause,
__builtin_riscv_sha256sig0,
__builtin_riscv_sha256sig1,
__builtin_riscv_sha256sum0,
__builtin_riscv_sha256sum1,
__builtin_riscv_sha512sig0,
__builtin_riscv_sha512sig0h,
__builtin_riscv_sha512sig0l,
__builtin_riscv_sha512sig1,
__builtin_riscv_sha512sig1h,
__builtin_riscv_sha512sig1l,
__builtin_riscv_sha512sum0,
__builtin_riscv_sha512sum0r,
__builtin_riscv_sha512sum1,
__builtin_riscv_sha512sum1r,
__builtin_riscv_sm3p0,
__builtin_riscv_sm3p1,
__builtin_riscv_sm4ed,
__builtin_riscv_sm4ks,
__builtin_riscv_unzip_32,
__builtin_riscv_xperm4_32,
__builtin_riscv_xperm4_64,
__builtin_riscv_xperm8_32,
__builtin_riscv_xperm8_64,
__builtin_riscv_zip_32,
};
pub fn fromName(name: []const u8) ?Properties {
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) Properties {
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;
}
pub const shortest_name = 21;
pub const longest_name = 29;
/// 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 {
@setEvalBranchQuota(136);
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 w = std.Io.Writer.fixed(buf);
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 w.buffered();
}
const Node = packed struct {
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: std.math.IntFittingRange(0, data.len),
/// 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,
/// Index of the first child of this node.
child_index: u16,
};
const dafsa = [_]Node{
.{ .char = 0, .end_of_word = false, .end_of_list = true, .number = 0, .child_index = 1 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 2 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 3 },
.{ .char = 'b', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 4 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 5 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 6 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 7 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 8 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 9 },
.{ .char = 'n', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 10 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 11 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 12 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 13 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 14 },
.{ .char = 'c', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 15 },
.{ .char = 'v', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 16 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 68, .child_index = 17 },
.{ .char = 'a', .end_of_word = false, .end_of_list = false, .number = 11, .child_index = 27 },
.{ .char = 'b', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 28 },
.{ .char = 'c', .end_of_word = false, .end_of_list = false, .number = 26, .child_index = 29 },
.{ .char = 'n', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 32 },
.{ .char = 'o', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 33 },
.{ .char = 'p', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 34 },
.{ .char = 's', .end_of_word = false, .end_of_list = false, .number = 18, .child_index = 35 },
.{ .char = 'u', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 37 },
.{ .char = 'x', .end_of_word = false, .end_of_list = false, .number = 4, .child_index = 38 },
.{ .char = 'z', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 39 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 11, .child_index = 40 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 41 },
.{ .char = 'l', .end_of_word = false, .end_of_list = false, .number = 8, .child_index = 42 },
.{ .char = 't', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 44 },
.{ .char = 'v', .end_of_word = false, .end_of_list = true, .number = 16, .child_index = 45 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 46 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 47 },
.{ .char = 'a', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 48 },
.{ .char = 'h', .end_of_word = false, .end_of_list = false, .number = 14, .child_index = 49 },
.{ .char = 'm', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 50 },
.{ .char = 'n', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 52 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 53 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 54 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 11, .child_index = 55 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 57 },
.{ .char = 'm', .end_of_word = false, .end_of_list = false, .number = 6, .child_index = 58 },
.{ .char = 'z', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 59 },
.{ .char = 'z', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 59 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 16, .child_index = 60 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 61 },
.{ .char = 'c', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 62 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 63 },
.{ .char = 'a', .end_of_word = false, .end_of_list = true, .number = 14, .child_index = 64 },
.{ .char = '3', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 66 },
.{ .char = '4', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 67 },
.{ .char = 'z', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 39 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 69 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 70 },
.{ .char = '3', .end_of_word = false, .end_of_list = false, .number = 4, .child_index = 71 },
.{ .char = '6', .end_of_word = false, .end_of_list = true, .number = 7, .child_index = 72 },
.{ .char = 'v', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 73 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 6, .child_index = 74 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 75 },
.{ .char = 'a', .end_of_word = false, .end_of_list = true, .number = 16, .child_index = 77 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 78 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 80 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 81 },
.{ .char = '2', .end_of_word = false, .end_of_list = false, .number = 4, .child_index = 82 },
.{ .char = '5', .end_of_word = false, .end_of_list = true, .number = 10, .child_index = 83 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 84 },
.{ .char = 'e', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 86 },
.{ .char = 'k', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 87 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 88 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 89 },
.{ .char = '2', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 90 },
.{ .char = '4', .end_of_word = false, .end_of_list = true, .number = 7, .child_index = 92 },
.{ .char = '8', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 59 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 6, .child_index = 96 },
.{ .char = '3', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 99 },
.{ .char = '6', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 100 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 16, .child_index = 101 },
.{ .char = 'l', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 102 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 103 },
.{ .char = 'b', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 59 },
.{ .char = 'e', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = '5', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 104 },
.{ .char = '1', .end_of_word = false, .end_of_list = true, .number = 10, .child_index = 105 },
.{ .char = '0', .end_of_word = true, .end_of_list = false, .number = 1, .child_index = 0 },
.{ .char = '1', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'd', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 's', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'm', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 106 },
.{ .char = '3', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 99 },
.{ .char = 'd', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 108 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 108 },
.{ .char = 'd', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 109 },
.{ .char = 'e', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 109 },
.{ .char = 'i', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 110 },
.{ .char = 'k', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 111 },
.{ .char = '_', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 75 },
.{ .char = 'h', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 59 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 59 },
.{ .char = '2', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = '4', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 16, .child_index = 112 },
.{ .char = 'o', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 113 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 114 },
.{ .char = '6', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 115 },
.{ .char = '2', .end_of_word = false, .end_of_list = true, .number = 10, .child_index = 116 },
.{ .char = '4', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 59 },
.{ .char = '8', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 59 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 117 },
.{ .char = 's', .end_of_word = true, .end_of_list = true, .number = 2, .child_index = 119 },
.{ .char = 'm', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 120 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 16, .child_index = 122 },
.{ .char = 'a', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 86 },
.{ .char = 'o', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 126 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 127 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 10, .child_index = 129 },
.{ .char = 'i', .end_of_word = true, .end_of_list = false, .number = 1, .child_index = 0 },
.{ .char = 'm', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 131 },
.{ .char = 'm', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = '1', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 131 },
.{ .char = '2', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'a', .end_of_word = false, .end_of_list = false, .number = 4, .child_index = 132 },
.{ .char = 'c', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 133 },
.{ .char = 'e', .end_of_word = false, .end_of_list = false, .number = 4, .child_index = 134 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 6, .child_index = 135 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 81 },
.{ .char = 'i', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 137 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 138 },
.{ .char = 'i', .end_of_word = false, .end_of_list = false, .number = 6, .child_index = 139 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 140 },
.{ .char = 'i', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'd', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 141 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 142 },
.{ .char = 'x', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 143 },
.{ .char = 'l', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 144 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 145 },
.{ .char = 'g', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 84 },
.{ .char = 'm', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 84 },
.{ .char = 'g', .end_of_word = false, .end_of_list = true, .number = 6, .child_index = 146 },
.{ .char = 'm', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 148 },
.{ .char = 'd', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 150 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 153 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 154 },
.{ .char = 'e', .end_of_word = true, .end_of_list = true, .number = 2, .child_index = 156 },
.{ .char = 'b', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 150 },
.{ .char = '0', .end_of_word = true, .end_of_list = false, .number = 3, .child_index = 157 },
.{ .char = '1', .end_of_word = true, .end_of_list = true, .number = 3, .child_index = 157 },
.{ .char = '0', .end_of_word = true, .end_of_list = false, .number = 2, .child_index = 159 },
.{ .char = '1', .end_of_word = true, .end_of_list = true, .number = 2, .child_index = 159 },
.{ .char = 'N', .end_of_word = true, .end_of_list = false, .number = 1, .child_index = 0 },
.{ .char = 'R', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 160 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 161 },
.{ .char = 'p', .end_of_word = true, .end_of_list = true, .number = 2, .child_index = 156 },
.{ .char = 'b', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 163 },
.{ .char = 'h', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 163 },
.{ .char = 'u', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'h', .end_of_word = true, .end_of_list = false, .number = 1, .child_index = 0 },
.{ .char = 'l', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'r', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'N', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'N', .end_of_word = true, .end_of_list = false, .number = 1, .child_index = 0 },
.{ .char = 'R', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 160 },
.{ .char = 's', .end_of_word = true, .end_of_list = false, .number = 1, .child_index = 0 },
.{ .char = 'z', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
};
pub const data = blk: {
@setEvalBranchQuota(612);
break :blk [_]Properties{
.{ .param_str = "UiUiUiIUi", .features = "zknd,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiIUi", .features = "zknd,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiIUi", .features = "zkne,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiIUi", .features = "zkne,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zknd,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zknd,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zkne,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zkne,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zknd,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiIUi", .features = "zknd|zkne,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zknd|zkne,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zbkb", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zbkb,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zbc|zbkc", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zbc|zbkc,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zbc|zbkc,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zbc|zbkc,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zbc,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zbc,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zbb|xtheadbb", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUWi", .features = "zbb|xtheadbb,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zbb", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUWi", .features = "zbb,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "iiiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "iiiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "iii", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "ii", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "ii", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "iii", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "iUiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "iiiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "iiiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiUi", .features = "xcvalu", .attributes = .{ .@"const" = true } },
.{ .param_str = "v.", .features = "zihintntl", .attributes = .{ .custom_typecheck = true } },
.{ .param_str = "v.", .features = "zihintntl", .attributes = .{ .custom_typecheck = true } },
.{ .param_str = "UiUi", .features = "zbb", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zbb,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "v", .features = "zihintpause" },
.{ .param_str = "UiUi", .features = "zknh", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zknh", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zknh", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zknh", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zknh,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zknh,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zknh,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zknh,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zknh,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zknh,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zknh,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zknh,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWi", .features = "zknh,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zknh,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zksh", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zksh", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiIUi", .features = "zksed", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUiIUi", .features = "zksed", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zbkb,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zbkx,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zbkx,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUiUi", .features = "zbkx,32bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UWiUWiUWi", .features = "zbkx,64bit", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi", .features = "zbkb,32bit", .attributes = .{ .@"const" = true } },
};
};
};
}

1068
lib/compiler/aro/aro/Builtins/s390x.zig vendored Normal file

File diff suppressed because it is too large Load Diff

3370
lib/compiler/aro/aro/Builtins/ve.zig vendored Normal file

File diff suppressed because it is too large Load Diff

6379
lib/compiler/aro/aro/Builtins/x86.zig vendored Normal file

File diff suppressed because it is too large Load Diff

1122
lib/compiler/aro/aro/Builtins/x86_64.zig vendored Normal file

File diff suppressed because it is too large Load Diff

204
lib/compiler/aro/aro/Builtins/xcore.zig vendored Normal file
View File

@ -0,0 +1,204 @@
//! Autogenerated by GenerateDef from src/aro/Builtins/xcore.def, do not edit
// zig fmt: off
const std = @import("std");
pub fn with(comptime Properties: type) type {
return struct {
/// Integer starting at 0 derived from the unique index,
/// corresponds with the data array index.
pub const Tag = enum(u16) { __builtin_bitrev,
__builtin_getid,
__builtin_getps,
__builtin_setps,
};
pub fn fromName(name: []const u8) ?Properties {
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) Properties {
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;
}
pub const shortest_name = 15;
pub const longest_name = 16;
/// 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 {
@setEvalBranchQuota(8);
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 w = std.Io.Writer.fixed(buf);
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 w.buffered();
}
const Node = packed struct {
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: std.math.IntFittingRange(0, data.len),
/// 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,
/// Index of the first child of this node.
child_index: u16,
};
const dafsa = [_]Node{
.{ .char = 0, .end_of_word = false, .end_of_list = true, .number = 0, .child_index = 1 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 2 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 3 },
.{ .char = 'b', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 4 },
.{ .char = 'u', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 5 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 6 },
.{ .char = 'l', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 7 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 8 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 9 },
.{ .char = 'n', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 10 },
.{ .char = '_', .end_of_word = false, .end_of_list = true, .number = 4, .child_index = 11 },
.{ .char = 'b', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 14 },
.{ .char = 'g', .end_of_word = false, .end_of_list = false, .number = 2, .child_index = 15 },
.{ .char = 's', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 16 },
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 17 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 18 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 19 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 20 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 2, .child_index = 21 },
.{ .char = 't', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 23 },
.{ .char = 'r', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 24 },
.{ .char = 'i', .end_of_word = false, .end_of_list = false, .number = 1, .child_index = 25 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 26 },
.{ .char = 'p', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 26 },
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 27 },
.{ .char = 'd', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 's', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
.{ .char = 'v', .end_of_word = true, .end_of_list = true, .number = 1, .child_index = 0 },
};
pub const data = blk: {
@setEvalBranchQuota(36);
break :blk [_]Properties{
.{ .param_str = "UiUi", .attributes = .{ .@"const" = true } },
.{ .param_str = "Si", .attributes = .{ .@"const" = true } },
.{ .param_str = "UiUi" },
.{ .param_str = "vUiUi" },
};
};
};
}

View File

@ -2,13 +2,12 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const backend = @import("../backend.zig");
const backend = @import("backend");
const Interner = backend.Interner;
const Ir = backend.Ir;
const Builder = Ir.Builder;
const Builtins = @import("Builtins.zig");
const Builtin = Builtins.Builtin;
const Compilation = @import("Compilation.zig");
const StringId = @import("StringInterner.zig").StringId;
const Tree = @import("Tree.zig");
@ -111,6 +110,9 @@ pub fn genIr(tree: *const Tree) Compilation.Error!Ir {
error.FatalError => return error.FatalError,
error.OutOfMemory => return error.OutOfMemory,
},
.global_asm => {
return c.fail("TODO global assembly", .{});
},
else => unreachable,
}
}
@ -497,6 +499,7 @@ fn genExpr(c: *CodeGen, node_index: Node.Index) Error!Ir.Ref {
.goto_stmt,
.computed_goto_stmt,
.nullptr_literal,
.asm_stmt,
=> return c.fail("TODO CodeGen.genStmt {s}\n", .{@tagName(node)}),
.comma_expr => |bin| {
_ = try c.genExpr(bin.lhs);
@ -857,7 +860,7 @@ fn genExpr(c: *CodeGen, node_index: Node.Index) Error!Ir.Ref {
},
.builtin_call_expr => |call| {
const name = c.tree.tokSlice(call.builtin_tok);
const builtin = c.comp.builtins.lookup(name).builtin;
const builtin = c.comp.builtins.lookup(name);
return c.genBuiltinCall(builtin, call.args, call.qt);
},
.addr_of_label,
@ -1074,10 +1077,10 @@ fn genBoolExpr(c: *CodeGen, base: Node.Index, true_label: Ir.Ref, false_label: I
try c.addBranch(cmp, true_label, false_label);
}
fn genBuiltinCall(c: *CodeGen, builtin: Builtin, arg_nodes: []const Node.Index, qt: QualType) Error!Ir.Ref {
fn genBuiltinCall(c: *CodeGen, builtin: Builtins.Expanded, arg_nodes: []const Node.Index, qt: QualType) Error!Ir.Ref {
_ = arg_nodes;
_ = qt;
return c.fail("TODO CodeGen.genBuiltinCall {s}\n", .{Builtin.nameFromTag(builtin.tag).span()});
return c.fail("TODO CodeGen.genBuiltinCall {t}\n", .{builtin.tag});
}
fn genCall(c: *CodeGen, call: Node.Call) Error!Ir.Ref {

File diff suppressed because it is too large Load Diff

View File

@ -195,7 +195,9 @@ pub const Option = enum {
@"out-of-scope-function",
@"date-time",
@"variadic-macro-arguments-omitted",
@"attribute-todo",
@"pragma-once-outside-header",
@"underlying-atomic-qualifier-ignored",
@"underlying-cv-qualifier-ignored",
/// GNU extensions
pub const gnu = [_]Option{

View File

@ -15,7 +15,7 @@ const GCCVersion = @import("Driver/GCCVersion.zig");
const LangOpts = @import("LangOpts.zig");
const Preprocessor = @import("Preprocessor.zig");
const Source = @import("Source.zig");
const target_util = @import("target.zig");
const Target = @import("Target.zig");
const Toolchain = @import("Toolchain.zig");
const Tree = @import("Tree.zig");
@ -46,6 +46,10 @@ comp: *Compilation,
diagnostics: *Diagnostics,
inputs: std.ArrayList(Source) = .empty,
imacros: std.ArrayList(Source) = .empty,
implicit_includes: std.ArrayList(Source) = .empty,
/// List of includes that will be used to construct the compilation's search path
includes: std.ArrayList(Compilation.Include) = .empty,
link_objects: std.ArrayList([]const u8) = .empty,
output_name: ?[]const u8 = null,
sysroot: ?[]const u8 = null,
@ -64,6 +68,7 @@ verbose_ast: bool = false,
verbose_pp: bool = false,
verbose_ir: bool = false,
verbose_linker_args: bool = false,
verbose_search_path: bool = false,
nobuiltininc: bool = false,
nostdinc: bool = false,
nostdlibinc: bool = false,
@ -99,6 +104,8 @@ aro_name: []const u8 = "",
/// Value of -target passed via CLI
raw_target_triple: ?[]const u8 = null,
/// Value of -darwin-target-variant-triple passed via CLI
raw_darwin_variant_target_triple: ?[]const u8 = null,
/// Value of -mcpu passed via CLI
raw_cpu: ?[]const u8 = null,
@ -107,6 +114,7 @@ raw_cpu: ?[]const u8 = null,
use_assembly_backend: bool = false,
// linker options
use_linker: ?[]const u8 = null,
linker_path: ?[]const u8 = null,
nodefaultlibs: bool = false,
nolibc: bool = false,
@ -130,6 +138,9 @@ pub fn deinit(d: *Driver) void {
d.comp.gpa.free(obj);
}
d.inputs.deinit(d.comp.gpa);
d.imacros.deinit(d.comp.gpa);
d.implicit_includes.deinit(d.comp.gpa);
d.includes.deinit(d.comp.gpa);
d.link_objects.deinit(d.comp.gpa);
d.* = undefined;
}
@ -161,6 +172,8 @@ pub const usage =
\\
\\Compile options:
\\ -c, --compile Only run preprocess, compile, and assemble steps
\\ -darwin-target-variant-triple
\\ Specify the darwin target variant triple
\\ -fapple-kext Use Apple's kernel extensions ABI
\\ -fchar8_t Enable char8_t (enabled by default in C23 and later)
\\ -fno-char8_t Disable char8_t (disabled by default for pre-C23)
@ -212,6 +225,8 @@ pub const usage =
\\ --embed-dir=<dir> Add directory to `#embed` search path
\\ --emulate=[clang|gcc|msvc]
\\ Select which C compiler to emulate (default clang)
\\ -imacros <file> Include macros from <file> before parsing
\\ -include <file> Process <file> as if `#include "file"` appeared as the first line of the primary source file.
\\ -mabicalls Enable SVR4-style position-independent code (Mips only)
\\ -mno-abicalls Disable SVR4-style position-independent code (Mips only)
\\ -mcmodel=<code-model> Generate code for the given code model
@ -273,7 +288,8 @@ pub fn parseArgs(
macro_buf: *std.ArrayList(u8),
args: []const []const u8,
) (Compilation.Error || std.Io.Writer.Error)!bool {
const io = d.comp.io;
const gpa = d.comp.gpa;
var i: usize = 1;
var comment_arg: []const u8 = "";
var hosted: ?bool = null;
@ -310,7 +326,7 @@ pub fn parseArgs(
value = macro[some + 1 ..];
macro = macro[0..some];
}
try macro_buf.print(d.comp.gpa, "#define {s} {s}\n", .{ macro, value });
try macro_buf.print(gpa, "#define {s} {s}\n", .{ macro, value });
} else if (mem.startsWith(u8, arg, "-U")) {
var macro = arg["-U".len..];
if (macro.len == 0) {
@ -321,7 +337,7 @@ pub fn parseArgs(
}
macro = args[i];
}
try macro_buf.print(d.comp.gpa, "#undef {s}\n", .{macro});
try macro_buf.print(gpa, "#undef {s}\n", .{macro});
} else if (mem.eql(u8, arg, "-O")) {
d.comp.code_gen_options.optimization_level = .@"1";
} else if (mem.startsWith(u8, arg, "-O")) {
@ -334,6 +350,13 @@ pub fn parseArgs(
d.system_defines = .no_system_defines;
} else if (mem.eql(u8, arg, "-c") or mem.eql(u8, arg, "--compile")) {
d.only_compile = true;
} else if (mem.eql(u8, arg, "-darwin-target-variant-triple")) {
i += 1;
if (i >= args.len) {
try d.err("expected argument after -darwin-target-variant-triple", .{});
continue;
}
d.raw_darwin_variant_target_triple = args[i];
} else if (mem.eql(u8, arg, "-dD")) {
d.debug_dump_letters.d = true;
} else if (mem.eql(u8, arg, "-dM")) {
@ -508,7 +531,7 @@ pub fn parseArgs(
}
path = args[i];
}
try d.comp.include_dirs.append(d.comp.gpa, path);
try d.includes.append(gpa, .{ .kind = .normal, .path = path });
} else if (mem.startsWith(u8, arg, "-idirafter")) {
var path = arg["-idirafter".len..];
if (path.len == 0) {
@ -519,7 +542,7 @@ pub fn parseArgs(
}
path = args[i];
}
try d.comp.after_include_dirs.append(d.comp.gpa, path);
try d.includes.append(gpa, .{ .kind = .after, .path = path });
} else if (mem.startsWith(u8, arg, "-isystem")) {
var path = arg["-isystem".len..];
if (path.len == 0) {
@ -530,7 +553,7 @@ pub fn parseArgs(
}
path = args[i];
}
try d.comp.system_include_dirs.append(d.comp.gpa, path);
try d.includes.append(gpa, .{ .kind = .system, .path = path });
} else if (mem.startsWith(u8, arg, "-iquote")) {
var path = arg["-iquote".len..];
if (path.len == 0) {
@ -541,7 +564,7 @@ pub fn parseArgs(
}
path = args[i];
}
try d.comp.iquote_include_dirs.append(d.comp.gpa, path);
try d.includes.append(gpa, .{ .kind = .quote, .path = path });
} else if (mem.startsWith(u8, arg, "-F")) {
var path = arg["-F".len..];
if (path.len == 0) {
@ -552,7 +575,7 @@ pub fn parseArgs(
}
path = args[i];
}
try d.comp.framework_dirs.append(d.comp.gpa, path);
try d.includes.append(gpa, .{ .kind = .framework, .path = path });
} else if (mem.startsWith(u8, arg, "-iframework")) {
var path = arg["-iframework".len..];
if (path.len == 0) {
@ -563,9 +586,27 @@ pub fn parseArgs(
}
path = args[i];
}
try d.comp.system_framework_dirs.append(d.comp.gpa, path);
try d.includes.append(gpa, .{ .kind = .system_framework, .path = path });
} else if (option(arg, "-include") orelse option(arg, "--include")) |implicit_include| {
try d.addImplicitInclude(implicit_include);
} else if (mem.eql(u8, arg, "-include") or mem.eql(u8, arg, "--include")) {
i += 1;
if (i >= args.len) {
try d.err("expected argument after {s}", .{arg});
continue;
}
try d.addImplicitInclude(args[i]);
} else if (option(arg, "-imacros") orelse option(arg, "--imacros")) |imacro_path| {
try d.addImacros(imacro_path);
} else if (mem.eql(u8, arg, "-imacros") or mem.eql(u8, arg, "--imacros")) {
i += 1;
if (i >= args.len) {
try d.err("expected argument after {s}", .{arg});
continue;
}
try d.addImacros(args[i]);
} else if (option(arg, "--embed-dir=")) |path| {
try d.comp.embed_dirs.append(d.comp.gpa, path);
try d.comp.embed_dirs.append(gpa, path);
} else if (option(arg, "--emulate=")) |compiler_str| {
const compiler = std.meta.stringToEnum(LangOpts.Compiler, compiler_str) orelse {
try d.err("invalid compiler '{s}'", .{arg});
@ -592,6 +633,9 @@ pub fn parseArgs(
d.output_name = file;
} else if (option(arg, "--sysroot=")) |sysroot| {
d.sysroot = sysroot;
} else if (mem.eql(u8, arg, "-Wp,-v")) {
// TODO this is not how this argument should work
d.verbose_search_path = true;
} else if (mem.eql(u8, arg, "-pedantic")) {
d.diagnostics.state.extensions = .warning;
} else if (mem.eql(u8, arg, "-pedantic-errors")) {
@ -744,41 +788,22 @@ pub fn parseArgs(
try d.warn("unknown argument '{s}'", .{arg});
}
} else if (std.mem.endsWith(u8, arg, ".o") or std.mem.endsWith(u8, arg, ".obj")) {
try d.link_objects.append(d.comp.gpa, arg);
try d.link_objects.append(gpa, arg);
} else {
const source = d.addSource(arg) catch |er| {
return d.fatal("unable to add source file '{s}': {s}", .{ arg, errorDescription(er) });
};
try d.inputs.append(d.comp.gpa, source);
try d.inputs.append(gpa, source);
}
}
{
var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
const opts: std.Target.Query.ParseOptions = .{
.arch_os_abi = d.raw_target_triple orelse "native",
.cpu_features = d.raw_cpu,
.diagnostics = &diags,
};
const query = std.Target.Query.parse(opts) catch |er| switch (er) {
error.UnknownCpuModel => {
return d.fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
},
error.UnknownCpuFeature => {
return d.fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
},
error.UnknownArchitecture => {
return d.fatal("unknown architecture: '{s}'", .{diags.unknown_architecture_name.?});
},
else => |e| return d.fatal("unable to parse target query '{s}': {s}", .{
opts.arch_os_abi, @errorName(e),
}),
};
d.comp.target = std.zig.system.resolveTargetQuery(io, query) catch |e| {
return d.fatal("unable to resolve target: {s}", .{errorDescription(e)});
};
d.comp.target = try d.parseTarget(d.raw_target_triple orelse "native", d.raw_cpu);
if (d.raw_darwin_variant_target_triple) |darwin_triple| {
d.comp.darwin_target_variant = try d.parseTarget(darwin_triple, null);
}
}
if (emulate != null or d.raw_target_triple != null) {
d.comp.langopts.setEmulatedCompiler(emulate orelse target_util.systemCompiler(d.comp.target));
d.comp.langopts.setEmulatedCompiler(emulate orelse d.comp.target.systemCompiler());
switch (d.comp.langopts.emulate) {
.clang => try d.diagnostics.set("clang", .off),
.gcc => try d.diagnostics.set("gnu", .off),
@ -839,6 +864,23 @@ fn addSource(d: *Driver, path: []const u8) !Source {
return d.comp.addSourceFromPath(path);
}
fn findIncludeCLI(d: *Driver, path: []const u8, kind: []const u8) !Source {
const source = (d.comp.findInclude(path, .{ .id = .keyword_include, .source = .generated }, .cli, .first) catch |er|
return d.fatal("unable to add {s} file '{s}': {s}", .{ kind, path, errorDescription(er) })) orelse
return d.fatal("unable to add {s} file '{s}': NotFound", .{ kind, path });
return source;
}
fn addImplicitInclude(d: *Driver, path: []const u8) !void {
const source = try d.findIncludeCLI(path, "implicit include");
try d.implicit_includes.append(d.comp.gpa, source);
}
fn addImacros(d: *Driver, path: []const u8) !void {
const source = try d.findIncludeCLI(path, "imacros");
try d.imacros.append(d.comp.gpa, source);
}
pub fn err(d: *Driver, fmt: []const u8, args: anytype) Compilation.Error!void {
var sf = std.heap.stackFallback(1024, d.comp.gpa);
var allocating: std.Io.Writer.Allocating = .init(sf.get());
@ -857,13 +899,142 @@ pub fn warn(d: *Driver, fmt: []const u8, args: anytype) Compilation.Error!void {
try d.diagnostics.add(.{ .kind = .warning, .text = allocating.written(), .location = null });
}
pub fn unsupportedOptionForTarget(d: *Driver, target: std.Target, opt: []const u8) Compilation.Error!void {
fn unsupportedOptionForTarget(d: *Driver, target: *const Target, opt: []const u8) Compilation.Error!void {
try d.err(
"unsupported option '{s}' for target '{s}-{s}-{s}'",
.{ opt, @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
);
}
fn parseTarget(d: *Driver, arch_os_abi: []const u8, opt_cpu_features: ?[]const u8) Compilation.Error!Target {
var query: std.Target.Query = .{
.dynamic_linker = .init(null),
};
var vendor: Target.Vendor = .unknown;
var opt_sub_arch: ?Target.SubArch = null;
var it = mem.splitScalar(u8, arch_os_abi, '-');
const arch_name = it.first();
const arch_is_native = mem.eql(u8, arch_name, "native");
if (!arch_is_native) {
query.cpu_arch, opt_sub_arch = Target.parseArchName(arch_name) orelse {
return d.fatal("unknown architecture: '{s}'", .{arch_name});
};
}
const arch = query.cpu_arch orelse @import("builtin").cpu.arch;
const opt_os_text = blk: {
const opt_os_or_vendor = it.next();
if (opt_os_or_vendor) |os_or_vendor| {
if (Target.parseVendorName(os_or_vendor)) |parsed_vendor| {
vendor = parsed_vendor;
break :blk it.next();
}
}
break :blk opt_os_or_vendor;
};
if (opt_os_text) |os_text| {
var version_str: []const u8 = undefined;
Target.parseOs(&query, os_text, &version_str) catch |er| switch (er) {
error.UnknownOs => return d.fatal("unknown operating system '{s}'", .{os_text}),
error.InvalidOsVersion => return d.fatal("invalid operating system version '{s}'", .{version_str}),
};
}
const opt_abi_text = it.next();
if (opt_abi_text) |abi_text| {
var version_str: []const u8 = undefined;
Target.parseAbi(&query, abi_text, &version_str) catch |er| switch (er) {
error.UnknownAbi => return d.fatal("unknown ABI '{s}'", .{abi_text}),
error.InvalidAbiVersion => return d.fatal("invalid ABI version '{s}'", .{version_str}),
error.InvalidApiVersion => return d.fatal("invalid Android API version '{s}'", .{version_str}),
};
}
if (it.next() != null) {
return d.fatal("unexpected extra field in target: '{s}'", .{arch_os_abi});
}
if (opt_cpu_features) |cpu_features| {
const all_features = arch.allFeaturesList();
var index: usize = 0;
while (index < cpu_features.len and
cpu_features[index] != '+' and
cpu_features[index] != '-')
{
index += 1;
}
const cpu_name = cpu_features[0..index];
const add_set = &query.cpu_features_add;
const sub_set = &query.cpu_features_sub;
if (mem.eql(u8, cpu_name, "native")) {
query.cpu_model = .native;
} else if (mem.eql(u8, cpu_name, "baseline")) {
query.cpu_model = .baseline;
} else {
query.cpu_model = .{ .explicit = arch.parseCpuModel(cpu_name) catch |er| switch (er) {
error.UnknownCpuModel => return d.fatal("unknown CPU model: '{s}'", .{cpu_name}),
} };
}
if (opt_sub_arch) |sub_arch| {
if (sub_arch.toFeature(arch)) |feature| {
add_set.addFeature(feature);
}
}
while (index < cpu_features.len) {
const op = cpu_features[index];
const set = switch (op) {
'+' => add_set,
'-' => sub_set,
else => unreachable,
};
index += 1;
const start = index;
while (index < cpu_features.len and
cpu_features[index] != '+' and
cpu_features[index] != '-')
{
index += 1;
}
const feature_name = cpu_features[start..index];
for (all_features, 0..) |feature, feat_index_usize| {
const feat_index: std.Target.Cpu.Feature.Set.Index = @intCast(feat_index_usize);
if (mem.eql(u8, feature_name, feature.name)) {
set.addFeature(feat_index);
break;
}
} else {
return d.fatal("unknown CPU feature: '{s}'", .{feature_name});
}
}
} else if (opt_sub_arch) |sub_arch| {
if (sub_arch.toFeature(arch)) |feature| {
query.cpu_features_add.addFeature(feature);
}
}
const zig_target = std.zig.system.resolveTargetQuery(d.comp.io, query) catch |e|
return d.fatal("unable to resolve target: {s}", .{errorDescription(e)});
if (query.isNative()) {
if (zig_target.os.tag.isDarwin()) {
vendor = .apple;
}
}
return .{
.cpu = zig_target.cpu,
.vendor = vendor,
.os = zig_target.os,
.abi = zig_target.abi,
.ofmt = zig_target.ofmt,
.dynamic_linker = zig_target.dynamic_linker,
};
}
pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{ FatalError, OutOfMemory } {
var sf = std.heap.stackFallback(1024, d.comp.gpa);
var allocating: std.Io.Writer.Allocating = .init(sf.get());
@ -971,8 +1142,9 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8, comptime fast_
};
tc.defineSystemIncludes() catch |er| switch (er) {
error.OutOfMemory => return error.OutOfMemory,
error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}),
error.FatalError => return error.FatalError,
};
try d.comp.initSearchPath(d.includes.items, d.verbose_search_path);
const builtin_macros = d.comp.generateBuiltinMacros(d.system_defines) catch |er| switch (er) {
error.FileTooBig => return d.fatal("builtin macro source exceeded max size", .{}),
@ -1110,6 +1282,7 @@ fn processSource(
comptime fast_exit: bool,
asm_gen_fn: ?AsmCodeGenFn,
) !void {
const gpa = d.comp.gpa;
d.comp.generated_buf.items.len = 0;
const prev_total = d.diagnostics.errors;
@ -1118,7 +1291,7 @@ fn processSource(
var name_buf: [std.fs.max_name_bytes]u8 = undefined;
var opt_dep_file = try d.initDepFile(source, &name_buf, false);
defer if (opt_dep_file) |*dep_file| dep_file.deinit(d.comp.gpa);
defer if (opt_dep_file) |*dep_file| dep_file.deinit(gpa);
if (opt_dep_file) |*dep_file| pp.dep_file = dep_file;
@ -1138,7 +1311,13 @@ fn processSource(
}
}
try pp.preprocessSources(&.{ source, builtin, user_macros });
try pp.preprocessSources(.{
.main = source,
.builtin = builtin,
.command_line = user_macros,
.imacros = d.imacros.items,
.implicit_includes = d.implicit_includes.items,
});
var writer_buf: [4096]u8 = undefined;
if (opt_dep_file) |dep_file| {
@ -1219,8 +1398,8 @@ fn processSource(
.{},
);
const assembly = try asm_fn(d.comp.target, &tree);
defer assembly.deinit(d.comp.gpa);
const assembly = try asm_fn(d.comp.target.toZigTarget(), &tree);
defer assembly.deinit(gpa);
if (d.only_preprocess_and_compile) {
const out_file = d.comp.cwd.createFile(out_file_name, .{}) catch |er|
@ -1249,20 +1428,20 @@ fn processSource(
}
} else {
var ir = try tree.genIr();
defer ir.deinit(d.comp.gpa);
defer ir.deinit(gpa);
if (d.verbose_ir) {
var stdout = std.fs.File.stdout().writer(&writer_buf);
ir.dump(d.comp.gpa, d.detectConfig(stdout.file), &stdout.interface) catch {};
ir.dump(gpa, d.detectConfig(stdout.file), &stdout.interface) catch {};
}
var render_errors: Ir.Renderer.ErrorList = .{};
defer {
for (render_errors.values()) |msg| d.comp.gpa.free(msg);
render_errors.deinit(d.comp.gpa);
for (render_errors.values()) |msg| gpa.free(msg);
render_errors.deinit(gpa);
}
var obj = ir.render(d.comp.gpa, d.comp.target, &render_errors) catch |e| switch (e) {
var obj = ir.render(gpa, d.comp.target.toZigTarget(), &render_errors) catch |e| switch (e) {
error.OutOfMemory => return error.OutOfMemory,
error.LowerFail => {
return d.fatal(
@ -1286,8 +1465,8 @@ fn processSource(
if (fast_exit) std.process.exit(0); // Not linking, no need for cleanup.
return;
}
try d.link_objects.ensureUnusedCapacity(d.comp.gpa, 1);
d.link_objects.appendAssumeCapacity(try d.comp.gpa.dupe(u8, out_file_name));
try d.link_objects.ensureUnusedCapacity(gpa, 1);
d.link_objects.appendAssumeCapacity(try gpa.dupe(u8, out_file_name));
d.temp_file_count += 1;
if (fast_exit) {
try d.invokeLinker(tc, fast_exit);
@ -1357,17 +1536,17 @@ fn exitWithCleanup(d: *Driver, code: u8) noreturn {
/// Parses the various -fpic/-fPIC/-fpie/-fPIE arguments.
/// Then, smooshes them together with platform defaults, to decide whether
/// this compile should be using PIC mode or not.
/// Returns a tuple of ( backend.CodeGenOptions.PicLevel, IsPIE).
pub fn getPICMode(d: *Driver, lastpic: []const u8) Compilation.Error!struct { backend.CodeGenOptions.PicLevel, bool } {
const eqlIgnoreCase = std.ascii.eqlIgnoreCase;
const target = d.comp.target;
const is_pie_default = switch (target_util.isPIEDefault(target)) {
const target = &d.comp.target;
const is_pie_default = switch (target.isPIEDefault()) {
.yes => true,
.no => false,
.depends_on_linker => false,
};
const is_pic_default = switch (target_util.isPICdefault(target)) {
const is_pic_default = switch (target.isPICdefault()) {
.yes => true,
.no => false,
.depends_on_linker => false,
@ -1423,7 +1602,7 @@ pub fn getPICMode(d: *Driver, lastpic: []const u8) Compilation.Error!struct { ba
// '-fno-...' arguments, both PIC and PIE are disabled. Any PIE
// option implicitly enables PIC at the same level.
if (target.os.tag == .windows and
!target_util.isCygwinMinGW(target) and
!target.isMinGW() and
(eqlIgnoreCase(lastpic, "-fpic") or eqlIgnoreCase(lastpic, "-fpie"))) // -fpic/-fPIC, -fpie/-fPIE
{
try d.unsupportedOptionForTarget(target, lastpic);
@ -1434,7 +1613,7 @@ pub fn getPICMode(d: *Driver, lastpic: []const u8) Compilation.Error!struct { ba
// Check whether the tool chain trumps the PIC-ness decision. If the PIC-ness
// is forced, then neither PIC nor PIE flags will have no effect.
const forced = switch (target_util.isPICDefaultForced(target)) {
const forced = switch (target.isPICDefaultForced()) {
.yes => true,
.no => false,
.depends_on_linker => false,
@ -1447,7 +1626,7 @@ pub fn getPICMode(d: *Driver, lastpic: []const u8) Compilation.Error!struct { ba
is_piclevel_two = mem.eql(u8, lastpic, "-fPIE") or mem.eql(u8, lastpic, "-fPIC");
} else {
pic, pie = .{ false, false };
if (target_util.isPS(target)) {
if (target.isPS()) {
if (d.comp.cmodel != .kernel) {
pic = true;
try d.warn(
@ -1459,7 +1638,7 @@ pub fn getPICMode(d: *Driver, lastpic: []const u8) Compilation.Error!struct { ba
}
}
if (pic and (target.os.tag.isDarwin() or target_util.isPS(target))) {
if (pic and (target.os.tag.isDarwin() or target.isPS())) {
is_piclevel_two = is_piclevel_two or is_pic_default;
}

View File

@ -2,7 +2,8 @@
const std = @import("std");
const mem = std.mem;
const Filesystem = @import("Filesystem.zig").Filesystem;
const Target = @import("../Target.zig");
const Toolchain = @import("../Toolchain.zig");
const MAX_BYTES = 1024; // TODO: Can we assume 1024 bytes enough for the info we need?
@ -168,9 +169,9 @@ fn scanForOsRelease(buf: []const u8) ?Tag {
return null;
}
fn detectOsRelease(fs: Filesystem) ?Tag {
fn detectOsRelease(tc: *const Toolchain) ?Tag {
var buf: [MAX_BYTES]u8 = undefined;
const data = fs.readFile("/etc/os-release", &buf) orelse fs.readFile("/usr/lib/os-release", &buf) orelse return null;
const data = tc.readFile("/etc/os-release", &buf) orelse tc.readFile("/usr/lib/os-release", &buf) orelse return null;
return scanForOsRelease(data);
}
@ -215,9 +216,9 @@ fn scanForLSBRelease(buf: []const u8) ?Tag {
return null;
}
fn detectLSBRelease(fs: Filesystem) ?Tag {
fn detectLSBRelease(tc: *const Toolchain) ?Tag {
var buf: [MAX_BYTES]u8 = undefined;
const data = fs.readFile("/etc/lsb-release", &buf) orelse return null;
const data = tc.readFile("/etc/lsb-release", &buf) orelse return null;
return scanForLSBRelease(data);
}
@ -233,9 +234,9 @@ fn scanForRedHat(buf: []const u8) Tag {
return .unknown;
}
fn detectRedhat(fs: Filesystem) ?Tag {
fn detectRedhat(tc: *const Toolchain) ?Tag {
var buf: [MAX_BYTES]u8 = undefined;
const data = fs.readFile("/etc/redhat-release", &buf) orelse return null;
const data = tc.readFile("/etc/redhat-release", &buf) orelse return null;
return scanForRedHat(data);
}
@ -269,21 +270,21 @@ fn scanForDebian(buf: []const u8) Tag {
return .unknown;
}
fn detectDebian(fs: Filesystem) ?Tag {
fn detectDebian(tc: *const Toolchain) ?Tag {
var buf: [MAX_BYTES]u8 = undefined;
const data = fs.readFile("/etc/debian_version", &buf) orelse return null;
const data = tc.readFile("/etc/debian_version", &buf) orelse return null;
return scanForDebian(data);
}
pub fn detect(target: std.Target, fs: Filesystem) Tag {
pub fn detect(target: *const Target, tc: *const Toolchain) Tag {
if (target.os.tag != .linux) return .unknown;
if (detectOsRelease(fs)) |tag| return tag;
if (detectLSBRelease(fs)) |tag| return tag;
if (detectRedhat(fs)) |tag| return tag;
if (detectDebian(fs)) |tag| return tag;
if (detectOsRelease(tc)) |tag| return tag;
if (detectLSBRelease(tc)) |tag| return tag;
if (detectRedhat(tc)) |tag| return tag;
if (detectDebian(tc)) |tag| return tag;
if (fs.exists("/etc/gentoo-release")) return .gentoo;
if (tc.exists("/etc/gentoo-release")) return .gentoo;
return .unknown;
}

View File

@ -1,5 +1,5 @@
const std = @import("std");
const Filesystem = @import("Filesystem.zig").Filesystem;
const Toolchain = @import("../Toolchain.zig");
/// Large enough for GCCDetector for Linux; may need to be increased to support other toolchains.
const max_multilibs = 4;
@ -10,10 +10,10 @@ pub const Detected = struct {
selected: Multilib = .{},
biarch_sibling: ?Multilib = null,
pub fn filter(d: *Detected, multilib_filter: Filter, fs: Filesystem) void {
pub fn filter(d: *Detected, multilib_filter: Filter, tc: *const Toolchain) void {
var found_count: u8 = 0;
for (d.multilibs()) |multilib| {
if (multilib_filter.exists(multilib, fs)) {
if (multilib_filter.exists(multilib, tc)) {
d.multilib_buf[found_count] = multilib;
found_count += 1;
}
@ -51,8 +51,8 @@ pub const Detected = struct {
pub const Filter = struct {
base: [2][]const u8,
file: []const u8,
pub fn exists(self: Filter, m: Multilib, fs: Filesystem) bool {
return fs.joinedExists(&.{ self.base[0], self.base[1], m.gcc_suffix, self.file });
pub fn exists(self: Filter, m: Multilib, tc: *const Toolchain) bool {
return tc.joinedExists(&.{ self.base[0], self.base[1], m.gcc_suffix, self.file });
}
};

View File

@ -22,11 +22,12 @@ const Identifier = struct {
byte_offset: u32 = 0,
fn slice(self: Identifier, comp: *const Compilation) []const u8 {
var tmp_tokenizer = Tokenizer{
var tmp_tokenizer: Tokenizer = .{
.buf = comp.getSource(self.id).buf,
.langopts = comp.langopts,
.index = self.byte_offset,
.source = .generated,
.splice_locs = &.{},
};
const res = tmp_tokenizer.next();
return tmp_tokenizer.buf[res.start..res.end];

File diff suppressed because it is too large Load Diff

View File

@ -1348,6 +1348,11 @@ pub const invalid_asm_str: Diagnostic = .{
.kind = .@"error",
};
pub const invalid_asm_output: Diagnostic = .{
.fmt = "invalid lvalue in asm output",
.kind = .@"error",
};
pub const dollar_in_identifier_extension: Diagnostic = .{
.fmt = "'$' in identifier",
.opt = .@"dollar-in-identifier-extension",
@ -1744,6 +1749,7 @@ pub const enum_fixed: Diagnostic = .{
.fmt = "enumeration types with a fixed underlying type are a Clang extension",
.kind = .off,
.opt = .@"fixed-enum-extension",
.suppress_version = .c23,
.extension = true,
};
@ -1767,6 +1773,29 @@ pub const enum_not_representable_fixed: Diagnostic = .{
.kind = .@"error",
};
pub const enum_forward_declaration: Diagnostic = .{
.fmt = "ISO C forbids forward references to 'enum' types",
.kind = .off,
.extension = true,
};
pub const enum_atomic_ignored: Diagnostic = .{
.fmt = "'_Atomic' qualifier ignored; operations involving the enumeration type will be non-atomic",
.kind = .@"error",
.opt = .@"underlying-atomic-qualifier-ignored",
};
pub const enum_qualifiers_ignored: Diagnostic = .{
.fmt = "qualifiers in enumeration underlying type ignored",
.kind = .warning,
.opt = .@"underlying-cv-qualifier-ignored",
};
pub const enum_invalid_underlying_type: Diagnostic = .{
.fmt = "non-integral type {qt} is an invalid underlying type",
.kind = .@"error",
};
pub const transparent_union_wrong_type: Diagnostic = .{
.fmt = "'transparent_union' attribute only applies to unions",
.opt = .@"ignored-attributes",
@ -2184,6 +2213,31 @@ pub const not_floating_type: Diagnostic = .{
.kind = .@"error",
};
pub const elementwise_type: Diagnostic = .{
.fmt = "argument must be a vector{s} (was '{qt}')",
.kind = .@"error",
};
pub const nontemporal_address_pointer: Diagnostic = .{
.fmt = "address argument to nontemporal builtin must be a pointer ('{qt}' invalid)",
.kind = .@"error",
};
pub const nontemporal_address_type: Diagnostic = .{
.fmt = "address argument to nontemporal builtin must be a pointer to integer, float, pointer, or a vector of such types ('{qt}' invalid)",
.kind = .@"error",
};
pub const atomic_address_pointer: Diagnostic = .{
.fmt = "address argument to atomic builtin must be a pointer ('{qt}' invalid)",
.kind = .@"error",
};
pub const atomic_address_type: Diagnostic = .{
.fmt = "address argument to atomic builtin must be a pointer to an integer or a pointer types ('{qt}' invalid)",
.kind = .@"error",
};
pub const argument_types_differ: Diagnostic = .{
.fmt = "arguments are of different types ({qt} vs {qt})",
.kind = .@"error",
@ -2304,12 +2358,6 @@ pub const overflow_result_requires_ptr: Diagnostic = .{
pub const attribute_todo: Diagnostic = .{
.fmt = "TODO: implement '{s}' attribute for {s}",
.kind = .warning,
.opt = .@"attribute-todo",
};
pub const invalid_type_underlying_enum: Diagnostic = .{
.fmt = "non-integral type {qt} is an invalid underlying type",
.kind = .@"error",
};
pub const auto_type_self_initialized: Diagnostic = .{
@ -2422,3 +2470,13 @@ pub const nonnull_not_applicable: Diagnostic = .{
.kind = .warning,
.opt = .@"ignored-attributes",
};
pub const mixing_decimal_floats: Diagnostic = .{
.fmt = "cannot mix operands of decimal floating and other floating types",
.kind = .@"error",
};
pub const invalid_attribute_location: Diagnostic = .{
.fmt = "{s} cannot appear here",
.kind = .@"error",
};

View File

@ -10,6 +10,13 @@ pub const Error = Compilation.Error || error{ UnknownPragma, StopPreprocessing }
const Pragma = @This();
/// A do-nothing pragma; useful for unwrapping an optional pragma into a pragma that does nothing in the null case.
pub const do_nothing: Pragma = .{
.deinit = deinit_nothing,
};
fn deinit_nothing(_: *Pragma, _: *Compilation) void {}
/// Called during Preprocessor.init
beforePreprocess: ?*const fn (*Pragma, *Compilation) void = null,
@ -40,6 +47,12 @@ preserveTokens: ?*const fn (*Pragma, *Preprocessor, start_idx: TokenIndex) bool
/// The parser's `p.tok_i` field must not be changed
parserHandler: ?*const fn (*Pragma, *Parser, start_idx: TokenIndex) Compilation.Error!void = null,
/// Whether to perform preprocessor token expansion on the token at index `i`. 0 is the index of the first
/// token after the name token (the name token is never expanded). Whitespace tokens are always skipped when calculating
/// token indices. For example, in `#pragma GCC warning "A warning"` token 0 is `warning` and token 1 is `"A warning"`
/// By default, all tokens are expanded; use this to override that behavior.
shouldExpandTokenAtIndexHandler: ?*const fn (*const Pragma, i: TokenIndex) bool = null,
pub fn pasteTokens(pp: *Preprocessor, start_idx: TokenIndex) ![]const u8 {
if (pp.tokens.get(start_idx).id == .nl) return error.ExpectedStringLiteral;
@ -84,6 +97,11 @@ pub fn parserCB(self: *Pragma, p: *Parser, start_idx: TokenIndex) Compilation.Er
if (self.parserHandler) |func| return func(self, p, start_idx);
}
pub fn shouldExpandTokenAtIndex(self: *const Pragma, idx: TokenIndex) bool {
if (self.shouldExpandTokenAtIndexHandler) |func| return func(self, idx);
return true;
}
pub const Diagnostic = struct {
fmt: []const u8,
kind: Diagnostics.Message.Kind,

File diff suppressed because it is too large Load Diff

View File

@ -73,6 +73,16 @@ pub const line_invalid_filename: Diagnostic = .{
.kind = .@"error",
};
pub const line_invalid_number: Diagnostic = .{
.fmt = "{s} directive requires a positive integer argument",
.kind = .@"error",
};
pub const line_invalid_flag: Diagnostic = .{
.fmt = "invalid flag '{s}' in line marker directive",
.kind = .@"error",
};
pub const unterminated_conditional_directive: Diagnostic = .{
.fmt = "unterminated conditional directive",
.kind = .@"error",
@ -456,3 +466,9 @@ pub const no_argument_variadic_macro: Diagnostic = .{
.kind = .off,
.extension = true,
};
pub const pragma_once_in_main_file: Diagnostic = .{
.fmt = "#pragma once in main file",
.kind = .warning,
.opt = .@"pragma-once-outside-header",
};

View File

@ -1,9 +1,15 @@
const std = @import("std");
pub const Id = enum(u32) {
unused = 0,
generated = 1,
pub const Id = packed struct(u32) {
index: enum(u31) {
unused = std.math.maxInt(u31) - 0,
generated = std.math.maxInt(u31) - 1,
_,
},
alias: bool = false,
pub const unused: Id = .{ .index = .unused };
pub const generated: Id = .{ .index = .generated };
};
/// Classifies the file for line marker output in -E mode
@ -52,20 +58,6 @@ id: Id,
splice_locs: []const u32,
kind: Kind,
/// Todo: binary search instead of scanning entire `splice_locs`.
pub fn numSplicesBefore(source: Source, byte_offset: u32) u32 {
for (source.splice_locs, 0..) |splice_offset, i| {
if (splice_offset > byte_offset) return @intCast(i);
}
return @intCast(source.splice_locs.len);
}
/// Returns the actual line number (before newline splicing) of a Location
/// This corresponds to what the user would actually see in their text editor
pub fn physicalLine(source: Source, loc: Location) u32 {
return loc.line + source.numSplicesBefore(loc.byte_offset);
}
pub fn lineCol(source: Source, loc: Location) ExpandedLocation {
var start: usize = 0;
// find the start of the line which is either a newline or a splice
@ -117,7 +109,7 @@ pub fn lineCol(source: Source, loc: Location) ExpandedLocation {
return .{
.path = source.path,
.line = source.buf[start..nl],
.line_no = loc.line + splice_index,
.line_no = loc.line,
.col = col,
.width = width,
.end_with_splice = end_with_splice,

1744
lib/compiler/aro/aro/Target.zig vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -144,6 +144,11 @@ pub const Token = struct {
hash,
hash_hash,
/// Special token for handling expansion of parameters to builtin preprocessor functions
macro_param_builtin_func,
/// Special token for implementing builtin object macros
macro_builtin_obj,
/// Special token to speed up preprocessing, `loc.end` will be an index to the param list.
macro_param,
/// Special token to signal that the argument must be replaced without expansion (e.g. in concatenation)
@ -154,40 +159,6 @@ pub const Token = struct {
stringify_va_args,
/// Special macro whitespace, always equal to a single space
macro_ws,
/// Special token for implementing __has_attribute
macro_param_has_attribute,
/// Special token for implementing __has_c_attribute
macro_param_has_c_attribute,
/// Special token for implementing __has_declspec_attribute
macro_param_has_declspec_attribute,
/// Special token for implementing __has_warning
macro_param_has_warning,
/// Special token for implementing __has_feature
macro_param_has_feature,
/// Special token for implementing __has_extension
macro_param_has_extension,
/// Special token for implementing __has_builtin
macro_param_has_builtin,
/// Special token for implementing __has_include
macro_param_has_include,
/// Special token for implementing __has_include_next
macro_param_has_include_next,
/// Special token for implementing __has_embed
macro_param_has_embed,
/// Special token for implementing __is_identifier
macro_param_is_identifier,
/// Special token for implementing __FILE__
macro_file,
/// Special token for implementing __LINE__
macro_line,
/// Special token for implementing __COUNTER__
macro_counter,
/// Special token for implementing _Pragma
macro_param_pragma_operator,
/// Special token for implementing __identifier (MS extension)
macro_param_ms_identifier,
/// Special token for implementing __pragma (MS extension)
macro_param_ms_pragma,
/// Special identifier for implementing __func__
macro_func,
@ -195,12 +166,6 @@ pub const Token = struct {
macro_function,
/// Special identifier for implementing __PRETTY_FUNCTION__
macro_pretty_func,
/// Special identifier for implementing __DATE__
macro_date,
/// Special identifier for implementing __TIME__
macro_time,
/// Special identifier for implementing __TIMESTAMP__
macro_timestamp,
keyword_auto,
keyword_auto_type,
@ -268,6 +233,17 @@ pub const Token = struct {
keyword_false,
keyword_nullptr,
keyword_typeof_unqual,
keyword_float16,
keyword_float32,
keyword_float64,
keyword_float128,
keyword_float32x,
keyword_float64x,
keyword_float128x,
keyword_dfloat32,
keyword_dfloat64,
keyword_dfloat128,
keyword_dfloat64x,
// Preprocessor directives
keyword_include,
@ -307,19 +283,17 @@ pub const Token = struct {
keyword_asm,
keyword_asm1,
keyword_asm2,
/// _Float128
keyword_float128_1,
/// __float128
keyword_float128_2,
keyword_float128_1,
keyword_int128,
keyword_imag1,
keyword_imag2,
keyword_real1,
keyword_real2,
keyword_float16,
// clang keywords
keyword_fp16,
keyword_bf16,
// ms keywords
keyword_declspec,
@ -375,6 +349,9 @@ pub const Token = struct {
/// completion of the preceding #include
include_resume,
/// Virtual linemarker token output from preprocessor to represent actual linemarker in the source file
linemarker,
/// A comment token if asked to preserve comments.
comment,
@ -408,9 +385,6 @@ pub const Token = struct {
.macro_func,
.macro_function,
.macro_pretty_func,
.macro_date,
.macro_time,
.macro_timestamp,
.keyword_auto,
.keyword_auto_type,
.keyword_break,
@ -480,7 +454,6 @@ pub const Token = struct {
.keyword_asm1,
.keyword_asm2,
.keyword_float128_1,
.keyword_float128_2,
.keyword_int128,
.keyword_imag1,
.keyword_imag2,
@ -488,6 +461,7 @@ pub const Token = struct {
.keyword_real2,
.keyword_float16,
.keyword_fp16,
.keyword_bf16,
.keyword_declspec,
.keyword_int64,
.keyword_int64_2,
@ -527,6 +501,16 @@ pub const Token = struct {
.keyword_false,
.keyword_nullptr,
.keyword_typeof_unqual,
.keyword_float32,
.keyword_float64,
.keyword_float128,
.keyword_float32x,
.keyword_float64x,
.keyword_float128x,
.keyword_dfloat32,
.keyword_dfloat64,
.keyword_dfloat128,
.keyword_dfloat64x,
=> return true,
else => return false,
}
@ -570,6 +554,7 @@ pub const Token = struct {
return switch (id) {
.include_start,
.include_resume,
.linemarker,
=> unreachable,
.unterminated_comment,
@ -605,27 +590,9 @@ pub const Token = struct {
.macro_param_no_expand,
.stringify_param,
.stringify_va_args,
.macro_param_has_attribute,
.macro_param_has_c_attribute,
.macro_param_has_declspec_attribute,
.macro_param_has_warning,
.macro_param_has_feature,
.macro_param_has_extension,
.macro_param_has_builtin,
.macro_param_has_include,
.macro_param_has_include_next,
.macro_param_has_embed,
.macro_param_is_identifier,
.macro_file,
.macro_line,
.macro_counter,
.macro_time,
.macro_date,
.macro_timestamp,
.macro_param_pragma_operator,
.macro_param_ms_identifier,
.macro_param_ms_pragma,
.placemarker,
.macro_param_builtin_func,
.macro_builtin_obj,
=> "",
.macro_ws => " ",
@ -744,6 +711,17 @@ pub const Token = struct {
.keyword_false => "false",
.keyword_nullptr => "nullptr",
.keyword_typeof_unqual => "typeof_unqual",
.keyword_float16 => "_Float16",
.keyword_float32 => "_Float32",
.keyword_float64 => "_Float64",
.keyword_float128 => "_Float128",
.keyword_float32x => "_Float32x",
.keyword_float64x => "_Float64x",
.keyword_float128x => "_Float128x",
.keyword_dfloat32 => "_Decimal32",
.keyword_dfloat64 => "_Decimal64",
.keyword_dfloat128 => "_Decimal128",
.keyword_dfloat64x => "_Decimal64x",
.keyword_include => "include",
.keyword_include_next => "include_next",
.keyword_embed => "embed",
@ -780,15 +758,14 @@ pub const Token = struct {
.keyword_asm => "asm",
.keyword_asm1 => "__asm",
.keyword_asm2 => "__asm__",
.keyword_float128_1 => "_Float128",
.keyword_float128_2 => "__float128",
.keyword_float128_1 => "__float128",
.keyword_int128 => "__int128",
.keyword_imag1 => "__imag",
.keyword_imag2 => "__imag__",
.keyword_real1 => "__real",
.keyword_real2 => "__real__",
.keyword_float16 => "_Float16",
.keyword_fp16 => "__fp16",
.keyword_bf16 => "__bf16",
.keyword_declspec => "__declspec",
.keyword_int64 => "__int64",
.keyword_int64_2 => "_int64",
@ -1030,6 +1007,17 @@ pub const Token = struct {
.{ "false", .keyword_false },
.{ "nullptr", .keyword_nullptr },
.{ "typeof_unqual", .keyword_typeof_unqual },
.{ "_Float16", .keyword_float16 },
.{ "_Float32", .keyword_float32 },
.{ "_Float64", .keyword_float64 },
.{ "_Float128", .keyword_float128 },
.{ "_Float32x", .keyword_float32x },
.{ "_Float64x", .keyword_float64x },
.{ "_Float128x", .keyword_float128x },
.{ "_Decimal32", .keyword_dfloat32 },
.{ "_Decimal64", .keyword_dfloat64 },
.{ "_Decimal128", .keyword_dfloat128 },
.{ "_Decimal64x", .keyword_dfloat64x },
// Preprocessor directives
.{ "include", .keyword_include },
@ -1073,17 +1061,16 @@ pub const Token = struct {
.{ "asm", .keyword_asm },
.{ "__asm", .keyword_asm1 },
.{ "__asm__", .keyword_asm2 },
.{ "_Float128", .keyword_float128_1 },
.{ "__float128", .keyword_float128_2 },
.{ "__float128", .keyword_float128_1 },
.{ "__int128", .keyword_int128 },
.{ "__imag", .keyword_imag1 },
.{ "__imag__", .keyword_imag2 },
.{ "__real", .keyword_real1 },
.{ "__real__", .keyword_real2 },
.{ "_Float16", .keyword_float16 },
// clang keywords
.{ "__fp16", .keyword_fp16 },
.{ "__bf16", .keyword_bf16 },
// ms keywords
.{ "__declspec", .keyword_declspec },
@ -1126,6 +1113,8 @@ index: u32 = 0,
source: Source.Id,
langopts: LangOpts,
line: u32 = 1,
splice_index: u32 = 0,
splice_locs: []const u32,
pub fn next(self: *Tokenizer) Token {
var state: enum {
@ -1909,6 +1898,12 @@ pub fn next(self: *Tokenizer) Token {
}
}
for (self.splice_locs[self.splice_index..]) |splice_offset| {
if (splice_offset > start) break;
self.line += 1;
self.splice_index += 1;
}
return .{
.id = id,
.start = start,
@ -2331,7 +2326,7 @@ test "Tokenizer fuzz test" {
fn testOne(_: @This(), input_bytes: []const u8) anyerror!void {
var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena.deinit();
var comp = Compilation.init(std.testing.allocator, arena.allocator(), undefined, std.fs.cwd());
var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, undefined, std.fs.cwd());
defer comp.deinit();
const source = try comp.addSourceFromBuffer("fuzz.c", input_bytes);
@ -2340,6 +2335,7 @@ test "Tokenizer fuzz test" {
.buf = source.buf,
.source = source.id,
.langopts = comp.langopts,
.splice_locs = &.{},
};
while (true) {
const prev_index = tokenizer.index;
@ -2355,16 +2351,17 @@ test "Tokenizer fuzz test" {
fn expectTokensExtra(contents: []const u8, expected_tokens: []const Token.Id, langopts: ?LangOpts) !void {
var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena.deinit();
var comp = Compilation.init(std.testing.allocator, arena.allocator(), undefined, std.fs.cwd());
var comp = Compilation.init(std.testing.allocator, arena.allocator(), std.testing.io, undefined, std.fs.cwd());
defer comp.deinit();
if (langopts) |provided| {
comp.langopts = provided;
}
const source = try comp.addSourceFromBuffer("path", contents);
var tokenizer = Tokenizer{
var tokenizer: Tokenizer = .{
.buf = source.buf,
.source = source.id,
.langopts = comp.langopts,
.splice_locs = &.{},
};
var i: usize = 0;
while (i < expected_tokens.len) {

View File

@ -5,9 +5,8 @@ const system_defaults = @import("system_defaults");
const Compilation = @import("Compilation.zig");
const Driver = @import("Driver.zig");
const Filesystem = @import("Driver/Filesystem.zig").Filesystem;
const Multilib = @import("Driver/Multilib.zig");
const target_util = @import("target.zig");
const Target = @import("Target.zig");
pub const PathList = std.ArrayList([]const u8);
@ -41,7 +40,6 @@ const Inner = union(enum) {
const Toolchain = @This();
filesystem: Filesystem,
driver: *Driver,
/// The list of toolchain specific path prefixes to search for libraries.
@ -57,8 +55,8 @@ selected_multilib: Multilib = .{},
inner: Inner = .{ .uninitialized = {} },
pub fn getTarget(tc: *const Toolchain) std.Target {
return tc.driver.comp.target;
pub fn getTarget(tc: *const Toolchain) *const Target {
return &tc.driver.comp.target;
}
fn getDefaultLinker(tc: *const Toolchain) []const u8 {
@ -107,7 +105,7 @@ pub fn getLinkerPath(tc: *const Toolchain, buf: []u8) ![]const u8 {
if (std.fs.path.dirname(path) == null) {
path = tc.getProgramPath(path, buf);
}
if (tc.filesystem.canExecute(path)) {
if (tc.canExecute(path)) {
return path;
}
}
@ -139,7 +137,7 @@ pub fn getLinkerPath(tc: *const Toolchain, buf: []u8) ![]const u8 {
}
if (std.fs.path.isAbsolute(use_linker)) {
if (tc.filesystem.canExecute(use_linker)) {
if (tc.canExecute(use_linker)) {
return use_linker;
}
} else {
@ -155,7 +153,7 @@ pub fn getLinkerPath(tc: *const Toolchain, buf: []u8) ![]const u8 {
}
linker_name.appendSliceAssumeCapacity(use_linker);
const linker_path = tc.getProgramPath(linker_name.items, buf);
if (tc.filesystem.canExecute(linker_path)) {
if (tc.canExecute(linker_path)) {
return linker_path;
}
}
@ -227,12 +225,12 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8
const candidate = std.fs.path.join(fib.allocator(), &.{ program_path, tool_name }) catch continue;
if (tc.filesystem.canExecute(candidate) and candidate.len <= buf.len) {
if (tc.canExecute(candidate) and candidate.len <= buf.len) {
@memcpy(buf[0..candidate.len], candidate);
return buf[0..candidate.len];
}
}
return tc.filesystem.findProgramByName(tc.driver.comp.gpa, name, tc.driver.comp.environment.path, buf) orelse continue;
return tc.findProgramByName(name, buf) orelse continue;
}
@memcpy(buf[0..name.len], name);
return buf[0..name.len];
@ -256,7 +254,7 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 {
// todo check compiler RT path
const aro_dir = std.fs.path.dirname(tc.driver.aro_name) orelse "";
const candidate = try std.fs.path.join(allocator, &.{ aro_dir, "..", name });
if (tc.filesystem.exists(candidate)) {
if (tc.exists(candidate)) {
return arena.dupe(u8, candidate);
}
@ -283,7 +281,7 @@ fn searchPaths(tc: *const Toolchain, fib: *std.heap.FixedBufferAllocator, sysroo
else
std.fs.path.join(fib.allocator(), &.{ path, name }) catch continue;
if (tc.filesystem.exists(candidate)) {
if (tc.exists(candidate)) {
return candidate;
}
}
@ -304,7 +302,7 @@ pub fn addPathIfExists(tc: *Toolchain, components: []const []const u8, dest_kind
const candidate = try std.fs.path.join(fib.allocator(), components);
if (tc.filesystem.exists(candidate)) {
if (tc.exists(candidate)) {
const duped = try tc.driver.comp.arena.dupe(u8, candidate);
const dest = switch (dest_kind) {
.library => &tc.library_paths,
@ -404,11 +402,11 @@ fn addUnwindLibrary(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !voi
const target = tc.getTarget();
if ((target.abi.isAndroid() and unw == .libgcc) or
target.ofmt == .wasm or
target_util.isWindowsMSVCEnvironment(target) or
target.isWindowsMSVCEnvironment() or
unw == .none) return;
const lgk = tc.getLibGCCKind();
const as_needed = lgk == .unspecified and !target.abi.isAndroid() and !target_util.isCygwinMinGW(target);
const as_needed = lgk == .unspecified and !target.abi.isAndroid() and !target.isMinGW();
try argv.ensureUnusedCapacity(tc.driver.comp.gpa, 3);
if (as_needed) {
@ -420,7 +418,7 @@ fn addUnwindLibrary(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !voi
.compiler_rt => if (lgk == .static) {
argv.appendAssumeCapacity("-l:libunwind.a");
} else if (lgk == .shared) {
if (target_util.isCygwinMinGW(target)) {
if (target.isMinGW()) {
argv.appendAssumeCapacity("-l:libunwind.dll.a");
} else {
argv.appendAssumeCapacity("-l:libunwind.so");
@ -455,7 +453,7 @@ pub fn addRuntimeLibs(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !v
// TODO
},
.libgcc => {
if (target_util.isKnownWindowsMSVCEnvironment(target)) {
if (target.isKnownWindowsMSVCEnvironment()) {
const rtlib_str = tc.driver.rtlib orelse system_defaults.rtlib;
if (!mem.eql(u8, rtlib_str, "platform")) {
try tc.driver.err("unsupported runtime library 'libgcc' for platform 'MSVC'", .{});
@ -477,14 +475,107 @@ pub fn defineSystemIncludes(tc: *Toolchain) !void {
.unknown => {
if (tc.driver.nostdinc) return;
const comp = tc.driver.comp;
if (!tc.driver.nobuiltininc) {
try comp.addBuiltinIncludeDir(tc.driver.aro_name, tc.driver.resource_dir);
try tc.addBuiltinIncludeDir();
}
if (!tc.driver.nostdlibinc) {
try comp.addSystemIncludeDir("/usr/include");
try tc.addSystemIncludeDir("/usr/include");
}
},
};
}
pub fn addSystemIncludeDir(tc: *const Toolchain, path: []const u8) !void {
const d = tc.driver;
_ = try d.includes.append(d.comp.gpa, .{ .kind = .system, .path = try d.comp.arena.dupe(u8, path) });
}
/// Add built-in aro headers directory to system include paths
pub fn addBuiltinIncludeDir(tc: *const Toolchain) !void {
const d = tc.driver;
const comp = d.comp;
const gpa = comp.gpa;
const arena = comp.arena;
try d.includes.ensureUnusedCapacity(gpa, 1);
if (d.resource_dir) |resource_dir| {
const path = try std.fs.path.join(arena, &.{ resource_dir, "include" });
comp.cwd.access(path, .{}) catch {
return d.fatal("Aro builtin headers not found in provided -resource-dir", .{});
};
d.includes.appendAssumeCapacity(.{ .kind = .system, .path = path });
return;
}
var search_path = d.aro_name;
while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) {
var base_dir = d.comp.cwd.openDir(dirname, .{}) catch continue;
defer base_dir.close();
base_dir.access("include/stddef.h", .{}) catch continue;
const path = try std.fs.path.join(arena, &.{ dirname, "include" });
d.includes.appendAssumeCapacity(.{ .kind = .system, .path = path });
break;
} else return d.fatal("unable to find Aro builtin headers", .{});
}
/// Read the file at `path` into `buf`.
/// Returns null if any errors are encountered
/// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned
pub fn readFile(tc: *const Toolchain, path: []const u8, buf: []u8) ?[]const u8 {
const comp = tc.driver.comp;
return comp.cwd.adaptToNewApi().readFile(comp.io, path, buf) catch null;
}
pub fn exists(tc: *const Toolchain, path: []const u8) bool {
const comp = tc.driver.comp;
comp.cwd.adaptToNewApi().access(comp.io, path, .{}) catch return false;
return true;
}
pub fn joinedExists(tc: *const Toolchain, parts: []const []const u8) bool {
var buf: [std.fs.max_path_bytes]u8 = undefined;
var fib = std.heap.FixedBufferAllocator.init(&buf);
const joined = std.fs.path.join(fib.allocator(), parts) catch return false;
return tc.exists(joined);
}
pub fn canExecute(tc: *const Toolchain, path: []const u8) bool {
if (@import("builtin").os.tag == .windows) {
// TODO
return true;
}
const comp = tc.driver.comp;
comp.cwd.adaptToNewApi().access(comp.io, path, .{ .execute = true }) catch return false;
// Todo: ensure path is not a directory
return true;
}
/// Search for an executable named `name` using platform-specific logic
/// If it's found, write the full path to `buf` and return a slice of it
/// Otherwise retun null
pub fn findProgramByName(tc: *const Toolchain, name: []const u8, buf: []u8) ?[]const u8 {
std.debug.assert(name.len > 0);
if (@import("builtin").os.tag == .windows) {
// TODO
return null;
}
const comp = tc.driver.comp;
// TODO: does WASI need special handling?
if (mem.indexOfScalar(u8, name, '/') != null) {
@memcpy(buf[0..name.len], name);
return buf[0..name.len];
}
const path_env = comp.environment.path orelse return null;
var fib = std.heap.FixedBufferAllocator.init(buf);
var it = mem.tokenizeScalar(u8, path_env, std.fs.path.delimiter);
while (it.next()) |path_dir| {
defer fib.reset();
const full_path = std.fs.path.join(fib.allocator(), &.{ path_dir, name }) catch continue;
if (tc.canExecute(full_path)) return full_path;
}
return null;
}

View File

@ -36,7 +36,7 @@ pub const TokenWithExpansionLocs = struct {
pub fn expansionSlice(tok: TokenWithExpansionLocs) []const Source.Location {
const locs = tok.expansion_locs orelse return &[0]Source.Location{};
var i: usize = 0;
while (locs[i].id != .unused) : (i += 1) {}
while (locs[i].id.index != .unused) : (i += 1) {}
return locs[0..i];
}
@ -56,7 +56,7 @@ pub const TokenWithExpansionLocs = struct {
if (tok.expansion_locs) |locs| {
var i: usize = 0;
while (locs[i].id != .unused) : (i += 1) {}
while (locs[i].id.index != .unused) : (i += 1) {}
list.items = locs[0..i];
while (locs[i].byte_offset != 1) : (i += 1) {}
list.capacity = i + 1;
@ -68,7 +68,7 @@ pub const TokenWithExpansionLocs = struct {
try list.ensureTotalCapacity(gpa, wanted_len);
for (new) |new_loc| {
if (new_loc.id == .generated) continue;
if (new_loc.id.index == .generated) continue;
list.appendAssumeCapacity(new_loc);
}
}
@ -76,7 +76,7 @@ pub const TokenWithExpansionLocs = struct {
pub fn free(expansion_locs: ?[*]Source.Location, gpa: std.mem.Allocator) void {
const locs = expansion_locs orelse return;
var i: usize = 0;
while (locs[i].id != .unused) : (i += 1) {}
while (locs[i].id.index != .unused) : (i += 1) {}
while (locs[i].byte_offset != 1) : (i += 1) {}
gpa.free(locs[0 .. i + 1]);
}
@ -133,10 +133,11 @@ pub fn deinit(tree: *Tree) void {
tree.* = undefined;
}
pub const GNUAssemblyQualifiers = struct {
pub const GNUAssemblyQualifiers = packed struct(u32) {
@"volatile": bool = false,
@"inline": bool = false,
goto: bool = false,
_: u29 = 0,
};
pub const Node = union(enum) {
@ -146,7 +147,7 @@ pub const Node = union(enum) {
param: Param,
variable: Variable,
typedef: Typedef,
global_asm: SimpleAsm,
global_asm: GlobalAsm,
struct_decl: ContainerDecl,
union_decl: ContainerDecl,
@ -173,7 +174,7 @@ pub const Node = union(enum) {
break_stmt: BreakStmt,
null_stmt: NullStmt,
return_stmt: ReturnStmt,
gnu_asm_simple: SimpleAsm,
asm_stmt: AsmStmt,
assign_expr: Binary,
mul_assign_expr: Binary,
@ -333,7 +334,7 @@ pub const Node = union(enum) {
implicit: bool,
};
pub const SimpleAsm = struct {
pub const GlobalAsm = struct {
asm_tok: TokenIndex,
asm_str: Node.Index,
};
@ -455,6 +456,22 @@ pub const Node = union(enum) {
},
};
pub const AsmStmt = struct {
asm_tok: TokenIndex,
asm_str: Node.Index,
outputs: []const Operand,
inputs: []const Operand,
clobbers: []const Node.Index,
labels: []const Node.Index,
quals: GNUAssemblyQualifiers,
pub const Operand = struct {
name: TokenIndex,
constraint: Node.Index,
expr: Node.Index,
};
};
pub const Binary = struct {
qt: QualType,
lhs: Node.Index,
@ -1027,10 +1044,56 @@ pub const Node = union(enum) {
},
},
},
.gnu_asm_simple => .{
.gnu_asm_simple = .{
.asm_stmt, .asm_stmt_volatile, .asm_stmt_inline, .asm_stmt_inline_volatile => |tag| {
const extra = tree.extra.items;
var extra_index = node_data[2];
const operand_size = @sizeOf(AsmStmt.Operand) / @sizeOf(u32);
const outputs_len = extra[extra_index] * operand_size;
extra_index += 1;
const inputs_len = extra[extra_index] * operand_size;
extra_index += 1;
const clobbers_len = extra[extra_index];
extra_index += 1;
const labels_len = node_data[1];
const quals: GNUAssemblyQualifiers = .{
.@"inline" = tag == .asm_stmt_inline or tag == .asm_stmt_inline_volatile,
.@"volatile" = tag == .asm_stmt_volatile or tag == .asm_stmt_inline_volatile,
.goto = labels_len > 0,
};
const outputs = extra[extra_index..][0..outputs_len];
extra_index += outputs_len;
const inputs = extra[extra_index..][0..inputs_len];
extra_index += inputs_len;
const clobbers = extra[extra_index..][0..clobbers_len];
extra_index += clobbers_len;
const labels = extra[extra_index..][0..labels_len];
extra_index += labels_len;
return .{
.asm_stmt = .{
.asm_tok = node_tok,
.asm_str = @enumFromInt(node_data[0]),
.outputs = @ptrCast(outputs),
.inputs = @ptrCast(inputs),
.clobbers = @ptrCast(clobbers),
.labels = @ptrCast(labels),
.quals = quals,
},
};
},
.asm_stmt_simple => .{
.asm_stmt = .{
.asm_tok = node_tok,
.asm_str = @enumFromInt(node_data[0]),
.outputs = &.{},
.inputs = &.{},
.clobbers = &.{},
.labels = &.{},
.quals = @bitCast(node_data[1]),
},
},
.assign_expr => .{
@ -1726,7 +1789,11 @@ pub const Node = union(enum) {
.computed_goto_stmt,
.continue_stmt,
.break_stmt,
.gnu_asm_simple,
.asm_stmt,
.asm_stmt_volatile,
.asm_stmt_inline,
.asm_stmt_inline_volatile,
.asm_stmt_simple,
.global_asm,
.generic_association_expr,
.generic_default_expr,
@ -1813,7 +1880,11 @@ pub const Node = union(enum) {
return_stmt,
return_none_stmt,
implicit_return,
gnu_asm_simple,
asm_stmt,
asm_stmt_inline,
asm_stmt_volatile,
asm_stmt_inline_volatile,
asm_stmt_simple,
comma_expr,
assign_expr,
mul_assign_expr,
@ -2174,10 +2245,39 @@ pub fn setNode(tree: *Tree, node: Node, index: usize) !void {
}
repr.tok = @"return".return_tok;
},
.gnu_asm_simple => |gnu_asm_simple| {
repr.tag = .gnu_asm_simple;
repr.data[0] = @intFromEnum(gnu_asm_simple.asm_str);
repr.tok = gnu_asm_simple.asm_tok;
.asm_stmt => |asm_stmt| {
repr.tok = asm_stmt.asm_tok;
repr.data[0] = @intFromEnum(asm_stmt.asm_str);
if (asm_stmt.outputs.len == 0 and asm_stmt.inputs.len == 0 and asm_stmt.clobbers.len == 0 and asm_stmt.labels.len == 0) {
repr.tag = .asm_stmt_simple;
repr.data[1] = @bitCast(asm_stmt.quals);
} else {
if (asm_stmt.quals.@"inline" and asm_stmt.quals.@"volatile") {
repr.tag = .asm_stmt_inline_volatile;
} else if (asm_stmt.quals.@"inline") {
repr.tag = .asm_stmt_inline;
} else if (asm_stmt.quals.@"volatile") {
repr.tag = .asm_stmt_volatile;
} else {
repr.tag = .asm_stmt;
}
repr.data[1] = @intCast(asm_stmt.labels.len);
repr.data[2] = @intCast(tree.extra.items.len);
const operand_size = @sizeOf(Node.AsmStmt.Operand) / @sizeOf(u32);
try tree.extra.ensureUnusedCapacity(tree.comp.gpa, 3 // lens
+ (asm_stmt.outputs.len + asm_stmt.inputs.len) * operand_size // outputs inputs
+ asm_stmt.clobbers.len + asm_stmt.labels.len);
tree.extra.appendAssumeCapacity(@intCast(asm_stmt.outputs.len));
tree.extra.appendAssumeCapacity(@intCast(asm_stmt.inputs.len));
tree.extra.appendAssumeCapacity(@intCast(asm_stmt.clobbers.len));
tree.extra.appendSliceAssumeCapacity(@ptrCast(asm_stmt.outputs));
tree.extra.appendSliceAssumeCapacity(@ptrCast(asm_stmt.inputs));
tree.extra.appendSliceAssumeCapacity(@ptrCast(asm_stmt.clobbers));
tree.extra.appendSliceAssumeCapacity(@ptrCast(asm_stmt.labels));
}
},
.assign_expr => |bin| {
repr.tag = .assign_expr;
@ -2804,18 +2904,17 @@ const CallableResultUsage = struct {
};
pub fn callableResultUsage(tree: *const Tree, node: Node.Index) ?CallableResultUsage {
var cur_node = node;
while (true) switch (cur_node.get(tree)) {
loop: switch (node.get(tree)) {
.decl_ref_expr => |decl_ref| return .{
.tok = decl_ref.name_tok,
.nodiscard = decl_ref.qt.hasAttribute(tree.comp, .nodiscard),
.warn_unused_result = decl_ref.qt.hasAttribute(tree.comp, .warn_unused_result),
},
.paren_expr, .addr_of_expr, .deref_expr => |un| cur_node = un.operand,
.comma_expr => |bin| cur_node = bin.rhs,
.cast => |cast| cur_node = cast.operand,
.call_expr => |call| cur_node = call.callee,
.paren_expr, .addr_of_expr, .deref_expr => |un| continue :loop un.operand.get(tree),
.comma_expr => |bin| continue :loop bin.rhs.get(tree),
.cast => |cast| continue :loop cast.operand.get(tree),
.call_expr => |call| continue :loop call.callee.get(tree),
.member_access_expr, .member_access_ptr_expr => |access| {
var qt = access.base.qt(tree);
if (qt.get(tree.comp, .pointer)) |pointer| qt = pointer.child;
@ -2825,19 +2924,14 @@ pub fn callableResultUsage(tree: *const Tree, node: Node.Index) ?CallableResultU
};
const field = record_ty.fields[access.member_index];
const attributes = field.attributes(tree.comp);
return .{
.tok = field.name_tok,
.nodiscard = for (attributes) |attr| {
if (attr.tag == .nodiscard) break true;
} else false,
.warn_unused_result = for (attributes) |attr| {
if (attr.tag == .warn_unused_result) break true;
} else false,
.nodiscard = field.qt.hasAttribute(tree.comp, .nodiscard),
.warn_unused_result = field.qt.hasAttribute(tree.comp, .warn_unused_result),
};
},
else => return null,
};
}
}
pub fn isLval(tree: *const Tree, node: Node.Index) bool {
@ -3004,6 +3098,13 @@ fn dumpNode(
try config.setColor(w, ATTRIBUTE);
try w.writeAll(" bitfield");
}
if (node == .asm_stmt) {
const quals = node.asm_stmt.quals;
try config.setColor(w, ATTRIBUTE);
if (quals.@"inline") try w.writeAll(" inline");
if (quals.@"volatile") try w.writeAll(" volatile");
if (quals.goto) try w.writeAll(" goto");
}
if (tree.value_map.get(node_index)) |val| {
try config.setColor(w, LITERAL);
@ -3028,7 +3129,6 @@ fn dumpNode(
if (node == .return_stmt and node.return_stmt.operand == .implicit and node.return_stmt.operand.implicit) {
try config.setColor(w, IMPLICIT);
try w.writeAll(" (value: 0)");
try config.setColor(w, .reset);
}
try w.writeAll("\n");
@ -3048,8 +3148,7 @@ fn dumpNode(
switch (node) {
.empty_decl => {},
.global_asm, .gnu_asm_simple => |@"asm"| {
try w.splatByteAll(' ', level + 1);
.global_asm => |@"asm"| {
try tree.dumpNode(@"asm".asm_str, level + delta, config, w);
},
.static_assert => |assert| {
@ -3428,6 +3527,73 @@ fn dumpNode(
.none => {},
}
},
.asm_stmt => |@"asm"| {
try tree.dumpNode(@"asm".asm_str, level + delta, config, w);
const write_operand = struct {
fn write_operand(
_w: *std.Io.Writer,
_level: u32,
_config: std.Io.tty.Config,
_tree: *const Tree,
operands: []const Node.AsmStmt.Operand,
) std.Io.tty.Config.SetColorError!void {
for (operands) |operand| {
if (operand.name != 0) {
try _w.splatByteAll(' ', _level + delta);
try _w.writeAll("asm name: ");
try _config.setColor(_w, NAME);
try _w.writeAll(_tree.tokSlice(operand.name));
try _w.writeByte('\n');
try _config.setColor(_w, .reset);
}
try _w.splatByteAll(' ', _level + delta);
try _w.writeAll("constraint: ");
const constraint_val = _tree.value_map.get(operand.constraint).?;
try _config.setColor(_w, LITERAL);
_ = try constraint_val.print(operand.constraint.qt(_tree), _tree.comp, _w);
try _w.writeByte('\n');
try _tree.dumpNode(operand.expr, _level + delta, _config, _w);
}
try _config.setColor(_w, .reset);
}
}.write_operand;
if (@"asm".outputs.len > 0) {
try w.splatByteAll(' ', level + half);
try w.writeAll("ouputs:\n");
try write_operand(w, level, config, tree, @"asm".outputs);
}
if (@"asm".inputs.len > 0) {
try w.splatByteAll(' ', level + half);
try w.writeAll("inputs:\n");
try write_operand(w, level, config, tree, @"asm".inputs);
}
if (@"asm".clobbers.len > 0) {
try w.splatByteAll(' ', level + half);
try w.writeAll("clobbers:\n");
try config.setColor(w, LITERAL);
for (@"asm".clobbers) |clobber| {
const clobber_val = tree.value_map.get(clobber).?;
try w.splatByteAll(' ', level + delta);
_ = try clobber_val.print(clobber.qt(tree), tree.comp, w);
try w.writeByte('\n');
}
try config.setColor(w, .reset);
}
if (@"asm".labels.len > 0) {
try w.splatByteAll(' ', level + half);
try w.writeAll("labels:\n");
for (@"asm".labels) |label| {
try tree.dumpNode(label, level + delta, config, w);
}
}
},
.call_expr => |call| {
try w.splatByteAll(' ', level + half);
try w.writeAll("callee:\n");

View File

@ -95,6 +95,33 @@ pub const Suffix = enum {
// _Bitint
WB, UWB,
// __bf16
BF16,
// _Float32 and imaginary _Float32
F32, IF32,
// _Float64 and imaginary _Float64
F64, IF64,
// _Float32x and imaginary _Float32x
F32x, IF32x,
// _Float64x and imaginary _Float64x
F64x, IF64x,
// _Decimal32
D32,
// _Decimal64
D64,
// _Decimal128
D128,
// _Decimal64x
D64x,
// zig fmt: on
const Tuple = struct { Suffix, []const []const u8 };
@ -126,6 +153,15 @@ pub const Suffix = enum {
.{ .W, &.{"W"} },
.{ .F128, &.{"F128"} },
.{ .Q, &.{"Q"} },
.{ .BF16, &.{"BF16"} },
.{ .F32, &.{"F32"} },
.{ .F64, &.{"F64"} },
.{ .F32x, &.{"F32x"} },
.{ .F64x, &.{"F64x"} },
.{ .D32, &.{"D32"} },
.{ .D64, &.{"D64"} },
.{ .D128, &.{"D128"} },
.{ .D64x, &.{"D64x"} },
.{ .I, &.{"I"} },
.{ .IL, &.{ "I", "L" } },
@ -134,6 +170,10 @@ pub const Suffix = enum {
.{ .IW, &.{ "I", "W" } },
.{ .IF128, &.{ "I", "F128" } },
.{ .IQ, &.{ "I", "Q" } },
.{ .IF32, &.{ "I", "F32" } },
.{ .IF64, &.{ "I", "F64" } },
.{ .IF32x, &.{ "I", "F32x" } },
.{ .IF64x, &.{ "I", "F64x" } },
};
pub fn fromString(buf: []const u8, suffix_kind: enum { int, float }) ?Suffix {
@ -162,8 +202,8 @@ pub const Suffix = enum {
pub fn isImaginary(suffix: Suffix) bool {
return switch (suffix) {
.I, .IL, .IF, .IU, .IUL, .ILL, .IULL, .IWB, .IUWB, .IF128, .IQ, .IW, .IF16 => true,
.None, .L, .F16, .F, .U, .UL, .LL, .ULL, .WB, .UWB, .F128, .Q, .W => false,
.I, .IL, .IF, .IU, .IUL, .ILL, .IULL, .IWB, .IUWB, .IF128, .IQ, .IW, .IF16, .IF32, .IF64, .IF32x, .IF64x => true,
.None, .L, .F16, .F, .U, .UL, .LL, .ULL, .WB, .UWB, .F128, .Q, .W, .F32, .F64, .F32x, .F64x, .D32, .D64, .D128, .D64x, .BF16 => false,
};
}
@ -171,7 +211,7 @@ pub const Suffix = enum {
return switch (suffix) {
.None, .L, .LL, .I, .IL, .ILL, .WB, .IWB => true,
.U, .UL, .ULL, .IU, .IUL, .IULL, .UWB, .IUWB => false,
.F, .IF, .F16, .F128, .IF128, .Q, .IQ, .W, .IW, .IF16 => unreachable,
.F, .IF, .F16, .F128, .IF128, .Q, .IQ, .W, .IW, .IF16, .F32, .IF32, .F64, .IF64, .F32x, .IF32x, .F64x, .IF64x, .D32, .D64, .D128, .D64x, .BF16 => unreachable,
};
}

View File

@ -7,7 +7,6 @@ const record_layout = @import("record_layout.zig");
const Parser = @import("Parser.zig");
const StringInterner = @import("StringInterner.zig");
const StringId = StringInterner.StringId;
const target_util = @import("target.zig");
const Tree = @import("Tree.zig");
const Node = Tree.Node;
const TokenIndex = Tree.TokenIndex;
@ -92,6 +91,16 @@ const Index = enum(u29) {
int_pointer = std.math.maxInt(u29) - 27,
/// Special type used when combining declarators.
declarator_combine = std.math.maxInt(u29) - 28,
float_bf16 = std.math.maxInt(u29) - 29,
float_float32 = std.math.maxInt(u29) - 30,
float_float64 = std.math.maxInt(u29) - 31,
float_float32x = std.math.maxInt(u29) - 32,
float_float64x = std.math.maxInt(u29) - 33,
float_float128x = std.math.maxInt(u29) - 34,
float_dfloat32 = std.math.maxInt(u29) - 35,
float_dfloat64 = std.math.maxInt(u29) - 36,
float_dfloat128 = std.math.maxInt(u29) - 37,
float_dfloat64x = std.math.maxInt(u29) - 38,
_,
};
@ -123,12 +132,22 @@ pub const QualType = packed struct(u32) {
pub const ulong_long: QualType = .{ ._index = .int_ulong_long };
pub const int128: QualType = .{ ._index = .int_int128 };
pub const uint128: QualType = .{ ._index = .int_uint128 };
pub const bf16: QualType = .{ ._index = .float_bf16 };
pub const fp16: QualType = .{ ._index = .float_fp16 };
pub const float16: QualType = .{ ._index = .float_float16 };
pub const float: QualType = .{ ._index = .float_float };
pub const double: QualType = .{ ._index = .float_double };
pub const long_double: QualType = .{ ._index = .float_long_double };
pub const float128: QualType = .{ ._index = .float_float128 };
pub const float32: QualType = .{ ._index = .float_float32 };
pub const float64: QualType = .{ ._index = .float_float64 };
pub const float32x: QualType = .{ ._index = .float_float32x };
pub const float64x: QualType = .{ ._index = .float_float64x };
pub const float128x: QualType = .{ ._index = .float_float128x };
pub const dfloat32: QualType = .{ ._index = .float_dfloat32 };
pub const dfloat64: QualType = .{ ._index = .float_dfloat64 };
pub const dfloat128: QualType = .{ ._index = .float_dfloat128 };
pub const dfloat64x: QualType = .{ ._index = .float_dfloat64x };
pub const void_pointer: QualType = .{ ._index = .void_pointer };
pub const char_pointer: QualType = .{ ._index = .char_pointer };
pub const int_pointer: QualType = .{ ._index = .int_pointer };
@ -184,12 +203,22 @@ pub const QualType = packed struct(u32) {
.int_ulong_long => return .{ .int = .ulong_long },
.int_int128 => return .{ .int = .int128 },
.int_uint128 => return .{ .int = .uint128 },
.float_bf16 => return .{ .float = .bf16 },
.float_fp16 => return .{ .float = .fp16 },
.float_float16 => return .{ .float = .float16 },
.float_float => return .{ .float = .float },
.float_double => return .{ .float = .double },
.float_long_double => return .{ .float = .long_double },
.float_float128 => return .{ .float = .float128 },
.float_float32 => return .{ .float = .float32 },
.float_float64 => return .{ .float = .float64 },
.float_float32x => return .{ .float = .float32x },
.float_float64x => return .{ .float = .float64x },
.float_float128x => return .{ .float = .float128x },
.float_dfloat32 => return .{ .float = .dfloat32 },
.float_dfloat64 => return .{ .float = .dfloat64 },
.float_dfloat128 => return .{ .float = .dfloat128 },
.float_dfloat64x => return .{ .float = .dfloat64x },
.void_pointer => return .{ .pointer = .{ .child = .void, .decayed = null } },
.char_pointer => return .{ .pointer = .{ .child = .char, .decayed = null } },
.int_pointer => return .{ .pointer = .{ .child = .int, .decayed = null } },
@ -608,8 +637,17 @@ pub const QualType = packed struct(u32) {
.float => comp.target.cTypeAlignment(.float),
.double => comp.target.cTypeAlignment(.double),
.long_double => comp.target.cTypeAlignment(.longdouble),
.fp16, .float16 => 2,
.bf16, .fp16, .float16 => 2,
.float128 => 16,
.float32 => comp.target.cTypeAlignment(.float),
.float64 => comp.target.cTypeAlignment(.double),
.float32x => 8,
.float64x => 16,
.float128x => unreachable, // Not supported
.dfloat32 => 4,
.dfloat64 => 8,
.dfloat128 => 16,
.dfloat64x => 16,
},
.bit_int => |bit_int| {
// https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2709.pdf
@ -628,7 +666,7 @@ pub const QualType = packed struct(u32) {
else => comp.target.ptrBitWidth() / 8,
},
.func => target_util.defaultFunctionAlignment(comp.target),
.func => comp.target.defaultFunctionAlignment(),
.array => |array| continue :loop array.elem.base(comp).type,
.vector => |vector| continue :loop vector.elem.base(comp).type,
@ -774,14 +812,23 @@ pub const QualType = packed struct(u32) {
pub fn floatRank(qt: QualType, comp: *const Compilation) usize {
return loop: switch (qt.base(comp).type) {
.float => |float_ty| switch (float_ty) {
// TODO: bfloat16 => 0
.bf16 => 0,
.float16 => 1,
.fp16 => 2,
.float => 3,
.double => 4,
.long_double => 5,
.float128 => 6,
.float32 => 4,
.float32x => 5,
.double => 6,
.float64 => 7,
.float64x => 8,
.long_double => 9,
.float128 => 10,
// TODO: ibm128 => 7
.float128x => unreachable, // Not supported
.dfloat32 => decimal_float_rank + 0,
.dfloat64 => decimal_float_rank + 1,
.dfloat64x => decimal_float_rank + 2,
.dfloat128 => decimal_float_rank + 3,
},
.complex => |complex| continue :loop complex.base(comp).type,
.atomic => |atomic| continue :loop atomic.base(comp).type,
@ -789,6 +836,8 @@ pub const QualType = packed struct(u32) {
};
}
pub const decimal_float_rank = 90;
/// Rank for integer conversions, ignoring domain (complex vs real)
/// Asserts that ty is an integer type
pub fn intRank(qt: QualType, comp: *const Compilation) usize {
@ -1135,7 +1184,7 @@ pub const QualType = packed struct(u32) {
if (index <= aligned_index) break;
}
last_aligned_index = index;
const requested = if (attribute.args.aligned.alignment) |alignment| alignment.requested else target_util.defaultAlignment(comp.target);
const requested = if (attribute.args.aligned.alignment) |alignment| alignment.requested else comp.target.defaultAlignment();
if (max_requested == null or max_requested.? < requested) {
max_requested = requested;
}
@ -1143,9 +1192,16 @@ pub const QualType = packed struct(u32) {
return max_requested;
}
pub fn linkage(qt: QualType, comp: *const Compilation) std.builtin.GlobalLinkage {
if (qt.hasAttribute(comp, .internal_linkage)) return .internal;
if (qt.hasAttribute(comp, .weak)) return .weak;
if (qt.hasAttribute(comp, .selectany)) return .link_once;
return .strong;
}
pub fn enumIsPacked(qt: QualType, comp: *const Compilation) bool {
std.debug.assert(qt.is(comp, .@"enum"));
return comp.langopts.short_enums or target_util.packAllEnums(comp.target) or qt.hasAttribute(comp, .@"packed");
return comp.langopts.short_enums or comp.target.packAllEnums() or qt.hasAttribute(comp, .@"packed");
}
pub fn shouldDesugar(qt: QualType, comp: *const Compilation) bool {
@ -1275,12 +1331,22 @@ pub const QualType = packed struct(u32) {
},
.bit_int => |bit_int| try w.print("{s} _BitInt({d})", .{ @tagName(bit_int.signedness), bit_int.bits }),
.float => |float_ty| switch (float_ty) {
.bf16 => try w.writeAll("__bf16"),
.fp16 => try w.writeAll("__fp16"),
.float16 => try w.writeAll("_Float16"),
.float => try w.writeAll("float"),
.double => try w.writeAll("double"),
.long_double => try w.writeAll("long double"),
.float128 => try w.writeAll("__float128"),
.float32 => try w.writeAll("_Float32"),
.float64 => try w.writeAll("_Float64"),
.float32x => try w.writeAll("_Float32x"),
.float64x => try w.writeAll("_Float64x"),
.float128x => try w.writeAll("_Float128x"),
.dfloat32 => try w.writeAll("_Decimal32"),
.dfloat64 => try w.writeAll("_Decimal64"),
.dfloat128 => try w.writeAll("_Decimal128"),
.dfloat64x => try w.writeAll("_Decimal64x"),
},
.complex => |complex| {
try w.writeAll("_Complex ");
@ -1498,21 +1564,41 @@ pub const Type = union(enum) {
};
pub const Float = enum {
bf16,
fp16,
float16,
float,
double,
long_double,
float128,
float32,
float64,
float32x,
float64x,
float128x,
dfloat32,
dfloat64,
dfloat128,
dfloat64x,
pub fn bits(float: Float, comp: *const Compilation) u16 {
return switch (float) {
.bf16 => 16,
.fp16 => 16,
.float16 => 16,
.float => comp.target.cTypeBitSize(.float),
.double => comp.target.cTypeBitSize(.double),
.long_double => comp.target.cTypeBitSize(.longdouble),
.float128 => 128,
.float32 => 32,
.float64 => 64,
.float32x => 32 * 2,
.float64x => 64 * 2,
.float128x => unreachable, // Not supported
.dfloat32 => 32,
.dfloat64 => 64,
.dfloat128 => 128,
.dfloat64x => 64 * 2,
};
}
};
@ -1747,12 +1833,22 @@ pub fn putExtra(ts: *TypeStore, gpa: std.mem.Allocator, ty: Type) !Index {
.uint128 => return .int_uint128,
},
.float => |float| switch (float) {
.bf16 => return .float_bf16,
.fp16 => return .float_fp16,
.float16 => return .float_float16,
.float => return .float_float,
.double => return .float_double,
.long_double => return .float_long_double,
.float128 => return .float_float128,
.float32 => return .float_float32,
.float64 => return .float_float64,
.float32x => return .float_float32x,
.float64x => return .float_float64x,
.float128x => return .float_float128x,
.dfloat32 => return .float_dfloat32,
.dfloat64 => return .float_dfloat64,
.dfloat128 => return .float_dfloat128,
.dfloat64x => return .float_dfloat64x,
},
else => {},
}
@ -2009,10 +2105,10 @@ pub fn initNamedTypes(ts: *TypeStore, comp: *Compilation) !void {
else => .int,
};
ts.intmax = target_util.intMaxType(comp.target);
ts.intptr = target_util.intPtrType(comp.target);
ts.int16 = target_util.int16Type(comp.target);
ts.int64 = target_util.int64Type(comp.target);
ts.intmax = comp.target.intMaxType();
ts.intptr = comp.target.intPtrType();
ts.int16 = comp.target.int16Type();
ts.int64 = comp.target.int64Type();
ts.uint_least16_t = comp.intLeastN(16, .unsigned);
ts.uint_least32_t = comp.intLeastN(32, .unsigned);
@ -2100,7 +2196,6 @@ fn generateVaListType(ts: *TypeStore, comp: *Compilation) !QualType {
else
return .char_pointer,
.powerpc, .powerpcle => .powerpc_va_list,
.s390x => .s390x_va_list,
.x86_64 => switch (comp.target.os.tag) {
.uefi, .windows => return .char_pointer,
else => .x86_64_va_list,
@ -2367,18 +2462,33 @@ pub const Builder = struct {
complex_sbit_int: u64,
complex_ubit_int: u64,
bf16,
fp16,
float16,
float,
double,
long_double,
float128,
float32,
float64,
float32x,
float64x,
float128x,
dfloat32,
dfloat64,
dfloat128,
dfloat64x,
complex,
complex_float16,
complex_float,
complex_double,
complex_long_double,
complex_float128,
complex_float32,
complex_float64,
complex_float32x,
complex_float64x,
complex_float128x,
// Any not simply constructed from specifier keywords.
other: QualType,
@ -2450,6 +2560,7 @@ pub const Builder = struct {
.complex_sint128 => "_Complex signed __int128",
.complex_uint128 => "_Complex unsigned __int128",
.bf16 => "__bf16",
.fp16 => "__fp16",
.float16 => "_Float16",
.float => "float",
@ -2581,18 +2692,33 @@ pub const Builder = struct {
break :blk if (complex) try qt.toComplex(b.parser.comp) else qt;
},
.bf16 => .bf16,
.fp16 => .fp16,
.float16 => .float16,
.float => .float,
.double => .double,
.long_double => .long_double,
.float128 => .float128,
.float32 => .float32,
.float64 => .float64,
.float32x => .float32x,
.float64x => .float64x,
.float128x => .float128x,
.dfloat32 => .dfloat32,
.dfloat64 => .dfloat64,
.dfloat128 => .dfloat128,
.dfloat64x => .dfloat64x,
.complex_float16,
.complex_float,
.complex_double,
.complex_long_double,
.complex_float128,
.complex_float32,
.complex_float64,
.complex_float32x,
.complex_float64x,
.complex_float128x,
.complex,
=> blk: {
const base_qt: QualType = switch (b.type) {
@ -2601,6 +2727,11 @@ pub const Builder = struct {
.complex_double => .double,
.complex_long_double => .long_double,
.complex_float128 => .float128,
.complex_float32 => .float32,
.complex_float64 => .float64,
.complex_float32x => .float32x,
.complex_float64x => .float64x,
.complex_float128x => .float128x,
.complex => .double,
else => unreachable,
};
@ -2749,7 +2880,7 @@ pub const Builder = struct {
else => {},
}
if (new == .int128 and !target_util.hasInt128(b.parser.comp.target)) {
if (new == .int128 and !b.parser.comp.target.hasInt128()) {
try b.parser.err(source_tok, .type_not_supported_on_target, .{"__int128"});
}
@ -2996,13 +3127,59 @@ pub const Builder = struct {
.complex => .complex_float128,
else => return b.cannotCombine(source_tok),
},
.complex => switch (b.type) {
.float32 => switch (b.type) {
.none => .float32,
.complex => .complex_float32,
else => return b.cannotCombine(source_tok),
},
.float64 => switch (b.type) {
.none => .float64,
.complex => .complex_float64,
else => return b.cannotCombine(source_tok),
},
.float32x => switch (b.type) {
.none => .float32x,
.complex => .complex_float32x,
else => return b.cannotCombine(source_tok),
},
.float64x => switch (b.type) {
.none => .float64x,
.complex => .complex_float64x,
else => return b.cannotCombine(source_tok),
},
.float128x => switch (b.type) {
.none => .float128x,
.complex => .complex_float128x,
else => return b.cannotCombine(source_tok),
},
.dfloat32 => switch (b.type) {
.none => .dfloat32,
else => return b.cannotCombine(source_tok),
},
.dfloat64 => switch (b.type) {
.none => .dfloat64,
else => return b.cannotCombine(source_tok),
},
.dfloat128 => switch (b.type) {
.none => .dfloat128,
else => return b.cannotCombine(source_tok),
},
.dfloat64x => switch (b.type) {
.none => .dfloat64x,
else => return b.cannotCombine(source_tok),
},
.complex => switch (b.type) { //
.none => .complex,
.float16 => .complex_float16,
.float => .complex_float,
.double => .complex_double,
.long_double => .complex_long_double,
.float128 => .complex_float128,
.float32 => .complex_float32,
.float64 => .complex_float64,
.float32x => .complex_float32x,
.float64x => .complex_float64x,
.float128x => .complex_float128x,
.char => .complex_char,
.schar => .complex_schar,
.uchar => .complex_uchar,
@ -3072,6 +3249,11 @@ pub const Builder = struct {
.complex_bit_int,
.complex_sbit_int,
.complex_ubit_int,
.complex_float32,
.complex_float64,
.complex_float32x,
.complex_float64x,
.complex_float128x,
=> return b.duplicateSpec(source_tok, "_Complex"),
else => return b.cannotCombine(source_tok),
},
@ -3104,12 +3286,22 @@ pub const Builder = struct {
return .{ .bit_int = bit_int.bits };
},
.float => |float| switch (float) {
.bf16 => .bf16,
.fp16 => .fp16,
.float16 => .float16,
.float => .float,
.double => .double,
.long_double => .long_double,
.float128 => .float128,
.float32 => .float32,
.float64 => .float64,
.float32x => .float32x,
.float64x => .float64x,
.float128x => .float128x,
.dfloat32 => .dfloat32,
.dfloat64 => .dfloat64,
.dfloat128 => .dfloat128,
.dfloat64x => .dfloat64x,
},
.complex => |complex| switch (complex.base(comp).type) {
.int => |int| switch (int) {
@ -3134,11 +3326,21 @@ pub const Builder = struct {
},
.float => |float| switch (float) {
.fp16 => unreachable,
.bf16 => unreachable,
.float16 => .complex_float16,
.float => .complex_float,
.double => .complex_double,
.long_double => .complex_long_double,
.float128 => .complex_float128,
.float32 => .complex_float32,
.float64 => .complex_float64,
.float32x => .complex_float32x,
.float64x => .complex_float64x,
.float128x => .complex_float128x,
.dfloat32 => unreachable,
.dfloat64 => unreachable,
.dfloat128 => unreachable,
.dfloat64x => unreachable,
},
else => unreachable,
},

View File

@ -8,7 +8,7 @@ const BigIntSpace = Interner.Tag.Int.BigIntSpace;
const annex_g = @import("annex_g.zig");
const Compilation = @import("Compilation.zig");
const target_util = @import("target.zig");
const Target = @import("Target.zig");
const QualType = @import("TypeStore.zig").QualType;
const Value = @This();
@ -80,10 +80,10 @@ test "minUnsignedBits" {
defer arena_state.deinit();
const arena = arena_state.allocator();
var comp = Compilation.init(std.testing.allocator, arena, undefined, std.fs.cwd());
var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, undefined, std.fs.cwd());
defer comp.deinit();
const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" });
comp.target = try std.zig.system.resolveTargetQuery(target_query);
comp.target = .fromZigTarget(try std.zig.system.resolveTargetQuery(std.testing.io, target_query));
try Test.checkIntBits(&comp, 0, 0);
try Test.checkIntBits(&comp, 1, 1);
@ -119,10 +119,10 @@ test "minSignedBits" {
defer arena_state.deinit();
const arena = arena_state.allocator();
var comp = Compilation.init(std.testing.allocator, arena, undefined, std.fs.cwd());
var comp = Compilation.init(std.testing.allocator, arena, std.testing.io, undefined, std.fs.cwd());
defer comp.deinit();
const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" });
comp.target = try std.zig.system.resolveTargetQuery(target_query);
comp.target = .fromZigTarget(try std.zig.system.resolveTargetQuery(std.testing.io, target_query));
try Test.checkIntBits(&comp, -1, 1);
try Test.checkIntBits(&comp, -2, 2);
@ -401,7 +401,7 @@ pub fn isZero(v: Value, comp: *const Compilation) bool {
switch (v.ref()) {
.zero => return true,
.one => return false,
.null => return target_util.nullRepr(comp.target) == 0,
.null => return comp.target.nullRepr() == 0,
else => {},
}
const key = comp.interner.get(v.ref());

View File

@ -1,6 +1,6 @@
const std = @import("std");
const Compilation = @import("Compilation.zig");
const target_util = @import("target.zig");
const Target = @import("Target.zig");
/// Used to implement the __has_feature macro.
pub fn hasFeature(comp: *Compilation, ext: []const u8) bool {
@ -43,7 +43,7 @@ pub fn hasFeature(comp: *Compilation, ext: []const u8) bool {
.c_atomic = comp.langopts.standard.atLeast(.c11),
.c_generic_selections = comp.langopts.standard.atLeast(.c11),
.c_static_assert = comp.langopts.standard.atLeast(.c11),
.c_thread_local = comp.langopts.standard.atLeast(.c11) and target_util.isTlsSupported(comp.target),
.c_thread_local = comp.langopts.standard.atLeast(.c11) and comp.target.isTlsSupported(),
};
inline for (@typeInfo(@TypeOf(list)).@"struct".fields) |f| {
if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name);
@ -60,7 +60,7 @@ pub fn hasExtension(comp: *Compilation, ext: []const u8) bool {
.c_atomic = true,
.c_generic_selections = true,
.c_static_assert = true,
.c_thread_local = target_util.isTlsSupported(comp.target),
.c_thread_local = comp.target.isTlsSupported(),
// misc
.overloadable_unmarked = false, // TODO
.statement_attributes_with_gnu_syntax = true,

View File

@ -18,6 +18,7 @@ pragma: Pragma = .{
.preprocessorHandler = preprocessorHandler,
.parserHandler = parserHandler,
.preserveTokens = preserveTokens,
.shouldExpandTokenAtIndexHandler = shouldExpandTokenAtIndex,
},
original_state: Diagnostics.State = .{},
state_stack: std.ArrayList(Diagnostics.State) = .empty,
@ -169,3 +170,7 @@ fn preserveTokens(_: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) bool {
}
return true;
}
fn shouldExpandTokenAtIndex(_: *const Pragma, _: TokenIndex) bool {
return false;
}

View File

@ -51,6 +51,15 @@ fn preprocessorHandler(pragma: *Pragma, pp: *Preprocessor, start_idx: TokenIndex
.location = name_tok.loc.expand(pp.comp),
}, pp.expansionSlice(start_idx + 1), true);
}
if (pp.include_depth == 0) {
const diagnostic: Preprocessor.Diagnostic = .pragma_once_in_main_file;
return pp.diagnostics.addWithLocation(pp.comp, .{
.text = diagnostic.fmt,
.kind = diagnostic.kind,
.opt = diagnostic.opt,
.location = name_tok.loc.expand(pp.comp),
}, pp.expansionSlice(start_idx + 1), true);
}
const seen = self.preprocess_count == pp.preprocess_count;
const prev = try self.pragma_once.fetchPut(pp.comp.gpa, name_tok.loc.id, {});
if (prev != null and !seen) {

View File

@ -84,10 +84,10 @@ fn parserHandler(pragma: *Pragma, p: *Parser, start_idx: TokenIndex) Compilation
if (action == .push) {
try pack.stack.append(p.comp.gpa, .{ .label = label orelse "", .val = p.pragma_pack orelse 8 });
} else {
pack.pop(p, label);
const pop_success = pack.pop(p, label);
if (new_val != null) {
try Pragma.err(p.pp, arg, .pragma_pack_undefined_pop, .{});
} else if (pack.stack.items.len == 0) {
} else if (!pop_success) {
try Pragma.err(p.pp, arg, .pragma_pack_empty_stack, .{});
}
}
@ -136,22 +136,25 @@ fn packInt(p: *Parser, tok_i: TokenIndex) Compilation.Error!?u8 {
}
}
fn pop(pack: *Pack, p: *Parser, maybe_label: ?[]const u8) void {
/// Returns true if an item was successfully popped.
fn pop(pack: *Pack, p: *Parser, maybe_label: ?[]const u8) bool {
if (maybe_label) |label| {
var i = pack.stack.items.len;
while (i > 0) {
i -= 1;
if (std.mem.eql(u8, pack.stack.items[i].label, label)) {
const prev = pack.stack.orderedRemove(i);
p.pragma_pack = prev.val;
return;
p.pragma_pack = pack.stack.items[i].val;
pack.stack.items.len = i;
return true;
}
}
return false;
} else {
const prev = pack.stack.pop() orelse {
p.pragma_pack = 2;
return;
return false;
};
p.pragma_pack = prev.val;
return true;
}
}

View File

@ -6,7 +6,7 @@ const std = @import("std");
const Attribute = @import("Attribute.zig");
const Compilation = @import("Compilation.zig");
const Parser = @import("Parser.zig");
const target_util = @import("target.zig");
const Target = @import("Target.zig");
const TypeStore = @import("TypeStore.zig");
const QualType = TypeStore.QualType;
const Type = TypeStore.Type;
@ -281,18 +281,18 @@ const SysVContext = struct {
// Some targets ignore the alignment of the underlying type when laying out
// non-zero-sized bit-fields. See test case 0072. On such targets, bit-fields never
// cross a storage boundary. See test case 0081.
if (target_util.ignoreNonZeroSizedBitfieldTypeAlignment(self.comp.target)) {
if (self.comp.target.ignoreNonZeroSizedBitfieldTypeAlignment()) {
ty_fld_algn_bits = 1;
}
} else {
// Some targets ignore the alignment of the underlying type when laying out
// zero-sized bit-fields. See test case 0073.
if (target_util.ignoreZeroSizedBitfieldTypeAlignment(self.comp.target)) {
if (self.comp.target.ignoreZeroSizedBitfieldTypeAlignment()) {
ty_fld_algn_bits = 1;
}
// Some targets have a minimum alignment of zero-sized bit-fields. See test case
// 0074.
if (target_util.minZeroWidthBitfieldAlignment(self.comp.target)) |target_align| {
if (self.comp.target.minZeroWidthBitfieldAlignment()) |target_align| {
ty_fld_algn_bits = @max(ty_fld_algn_bits, target_align);
}
}
@ -355,7 +355,7 @@ const SysVContext = struct {
// Unnamed fields do not contribute to the record alignment except on a few targets.
// See test case 0079.
if (is_named or target_util.unnamedFieldAffectsAlignment(self.comp.target)) {
if (is_named or self.comp.target.unnamedFieldAffectsAlignment()) {
var inherited_align_bits: u32 = undefined;
if (bit_width == 0) {

View File

@ -1,998 +0,0 @@
const std = @import("std");
const backend = @import("../backend.zig");
const LangOpts = @import("LangOpts.zig");
const TargetSet = @import("Builtins/Properties.zig").TargetSet;
const QualType = @import("TypeStore.zig").QualType;
/// intmax_t for this target
pub fn intMaxType(target: std.Target) QualType {
switch (target.cpu.arch) {
.aarch64,
.aarch64_be,
.sparc64,
=> if (target.os.tag != .openbsd) return .long,
.bpfel,
.bpfeb,
.loongarch64,
.riscv64,
.riscv64be,
.powerpc64,
.powerpc64le,
.ve,
=> return .long,
.x86_64 => switch (target.os.tag) {
.windows, .openbsd => {},
else => switch (target.abi) {
.gnux32, .muslx32 => {},
else => return .long,
},
},
else => {},
}
return .long_long;
}
/// intptr_t for this target
pub fn intPtrType(target: std.Target) QualType {
if (target.os.tag == .haiku) return .long;
switch (target.cpu.arch) {
.aarch64, .aarch64_be => switch (target.os.tag) {
.windows => return .long_long,
else => {},
},
.msp430,
.csky,
.loongarch32,
.riscv32,
.riscv32be,
.xcore,
.hexagon,
.m68k,
.spirv32,
.arc,
.avr,
=> return .int,
.sparc => switch (target.os.tag) {
.netbsd, .openbsd => {},
else => return .int,
},
.powerpc, .powerpcle => switch (target.os.tag) {
.linux, .freebsd, .netbsd => return .int,
else => {},
},
// 32-bit x86 Darwin, OpenBSD, and RTEMS use long (the default); others use int
.x86 => switch (target.os.tag) {
.openbsd, .rtems => {},
else => if (!target.os.tag.isDarwin()) return .int,
},
.x86_64 => switch (target.os.tag) {
.windows => return .long_long,
else => switch (target.abi) {
.gnux32, .muslx32 => return .int,
else => {},
},
},
else => {},
}
return .long;
}
/// int16_t for this target
pub fn int16Type(target: std.Target) QualType {
return switch (target.cpu.arch) {
.avr => .int,
else => .short,
};
}
/// sig_atomic_t for this target
pub fn sigAtomicType(target: std.Target) QualType {
if (target.cpu.arch.isWasm()) return .long;
return switch (target.cpu.arch) {
.avr => .schar,
.msp430 => .long,
else => .int,
};
}
/// int64_t for this target
pub fn int64Type(target: std.Target) QualType {
switch (target.cpu.arch) {
.loongarch64,
.ve,
.riscv64,
.riscv64be,
.powerpc64,
.powerpc64le,
.bpfel,
.bpfeb,
=> return .long,
.sparc64 => return intMaxType(target),
.x86, .x86_64 => if (!target.os.tag.isDarwin()) return intMaxType(target),
.aarch64, .aarch64_be => if (!target.os.tag.isDarwin() and target.os.tag != .openbsd and target.os.tag != .windows) return .long,
else => {},
}
return .long_long;
}
pub fn float80Type(target: std.Target) ?QualType {
switch (target.cpu.arch) {
.x86, .x86_64 => return .long_double,
else => {},
}
return null;
}
/// This function returns 1 if function alignment is not observable or settable.
pub fn defaultFunctionAlignment(target: std.Target) u8 {
return switch (target.cpu.arch) {
.arm, .armeb => 4,
.aarch64, .aarch64_be => 4,
.sparc, .sparc64 => 4,
.riscv64, .riscv64be => 2,
else => 1,
};
}
pub fn isTlsSupported(target: std.Target) bool {
if (target.os.tag.isDarwin()) {
var supported = false;
switch (target.os.tag) {
.macos => supported = !(target.os.isAtLeast(.macos, .{ .major = 10, .minor = 7, .patch = 0 }) orelse false),
else => {},
}
return supported;
}
return switch (target.cpu.arch) {
.bpfel, .bpfeb, .msp430, .nvptx, .nvptx64, .x86, .arm, .armeb, .thumb, .thumbeb => false,
else => true,
};
}
pub fn ignoreNonZeroSizedBitfieldTypeAlignment(target: std.Target) bool {
switch (target.cpu.arch) {
.avr => return true,
.arm => {
if (std.Target.arm.featureSetHas(target.cpu.features, .has_v7)) {
switch (target.os.tag) {
.ios => return true,
else => return false,
}
}
},
else => return false,
}
return false;
}
pub fn ignoreZeroSizedBitfieldTypeAlignment(target: std.Target) bool {
switch (target.cpu.arch) {
.avr => return true,
else => return false,
}
}
pub fn minZeroWidthBitfieldAlignment(target: std.Target) ?u29 {
switch (target.cpu.arch) {
.avr => return 8,
.arm => {
if (std.Target.arm.featureSetHas(target.cpu.features, .has_v7)) {
switch (target.os.tag) {
.ios => return 32,
else => return null,
}
} else return null;
},
else => return null,
}
}
pub fn unnamedFieldAffectsAlignment(target: std.Target) bool {
switch (target.cpu.arch) {
.aarch64 => {
if (target.os.tag.isDarwin() or target.os.tag == .windows) return false;
return true;
},
.armeb => {
if (std.Target.arm.featureSetHas(target.cpu.features, .has_v7)) {
if (std.Target.Abi.default(target.cpu.arch, target.os.tag) == .eabi) return true;
}
},
.arm => return true,
.avr => return true,
.thumb => {
if (target.os.tag == .windows) return false;
return true;
},
else => return false,
}
return false;
}
pub fn packAllEnums(target: std.Target) bool {
return switch (target.cpu.arch) {
.hexagon => true,
else => false,
};
}
/// Default alignment (in bytes) for __attribute__((aligned)) when no alignment is specified
pub fn defaultAlignment(target: std.Target) u29 {
switch (target.cpu.arch) {
.avr => return 1,
.arm => if (target.abi.isAndroid() or target.os.tag == .ios) return 16 else return 8,
.sparc => if (std.Target.sparc.featureSetHas(target.cpu.features, .v9)) return 16 else return 8,
.mips, .mipsel => switch (target.abi) {
.none, .gnuabi64 => return 16,
else => return 8,
},
.s390x, .armeb, .thumbeb, .thumb => return 8,
else => return 16,
}
}
pub fn systemCompiler(target: std.Target) LangOpts.Compiler {
// Android is linux but not gcc, so these checks go first
// the rest for documentation as fn returns .clang
if (target.os.tag.isDarwin() or
target.abi.isAndroid() or
target.os.tag.isBSD() or
target.os.tag == .fuchsia or
target.os.tag == .illumos or
target.os.tag == .haiku or
target.cpu.arch == .hexagon)
{
return .clang;
}
if (target.os.tag == .uefi) return .msvc;
// this is before windows to grab WindowsGnu
if (target.abi.isGnu() or
target.os.tag == .linux)
{
return .gcc;
}
if (target.os.tag == .windows) {
return .msvc;
}
if (target.cpu.arch == .avr) return .gcc;
return .clang;
}
pub fn hasFloat128(target: std.Target) bool {
if (target.cpu.arch.isWasm()) return true;
if (target.os.tag.isDarwin()) return false;
if (target.cpu.arch.isPowerPC()) return std.Target.powerpc.featureSetHas(target.cpu.features, .float128);
return switch (target.os.tag) {
.dragonfly,
.haiku,
.linux,
.openbsd,
.illumos,
=> target.cpu.arch.isX86(),
else => false,
};
}
pub fn hasInt128(target: std.Target) bool {
if (target.cpu.arch == .wasm32) return true;
if (target.cpu.arch == .x86_64) return true;
return target.ptrBitWidth() >= 64;
}
pub fn hasHalfPrecisionFloatABI(target: std.Target) bool {
return switch (target.cpu.arch) {
.thumb, .thumbeb, .arm, .aarch64 => true,
else => false,
};
}
pub const FPSemantics = enum {
None,
IEEEHalf,
BFloat,
IEEESingle,
IEEEDouble,
IEEEQuad,
/// Minifloat 5-bit exponent 2-bit mantissa
E5M2,
/// Minifloat 4-bit exponent 3-bit mantissa
E4M3,
x87ExtendedDouble,
IBMExtendedDouble,
/// Only intended for generating float.h macros for the preprocessor
pub fn forType(ty: std.Target.CType, target: std.Target) FPSemantics {
std.debug.assert(ty == .float or ty == .double or ty == .longdouble);
return switch (target.cTypeBitSize(ty)) {
32 => .IEEESingle,
64 => .IEEEDouble,
80 => .x87ExtendedDouble,
128 => switch (target.cpu.arch) {
.powerpc, .powerpcle, .powerpc64, .powerpc64le => .IBMExtendedDouble,
else => .IEEEQuad,
},
else => unreachable,
};
}
pub fn halfPrecisionType(target: std.Target) ?FPSemantics {
switch (target.cpu.arch) {
.aarch64,
.aarch64_be,
.arm,
.armeb,
.hexagon,
.riscv32,
.riscv32be,
.riscv64,
.riscv64be,
.spirv32,
.spirv64,
=> return .IEEEHalf,
.x86, .x86_64 => if (std.Target.x86.featureSetHas(target.cpu.features, .sse2)) return .IEEEHalf,
else => {},
}
return null;
}
pub fn chooseValue(self: FPSemantics, comptime T: type, values: [6]T) T {
return switch (self) {
.IEEEHalf => values[0],
.IEEESingle => values[1],
.IEEEDouble => values[2],
.x87ExtendedDouble => values[3],
.IBMExtendedDouble => values[4],
.IEEEQuad => values[5],
else => unreachable,
};
}
};
pub fn isLP64(target: std.Target) bool {
return target.cTypeBitSize(.int) == 32 and target.ptrBitWidth() == 64;
}
pub fn isKnownWindowsMSVCEnvironment(target: std.Target) bool {
return target.os.tag == .windows and target.abi == .msvc;
}
pub fn isWindowsMSVCEnvironment(target: std.Target) bool {
return target.os.tag == .windows and (target.abi == .msvc or target.abi == .none);
}
pub fn isCygwinMinGW(target: std.Target) bool {
return target.os.tag == .windows and (target.abi == .gnu);
}
pub fn isPS(target: std.Target) bool {
return (target.os.tag == .ps4 or target.os.tag == .ps5) and target.cpu.arch == .x86_64;
}
pub fn builtinEnabled(target: std.Target, enabled_for: TargetSet) bool {
var it = enabled_for.iterator();
while (it.next()) |val| {
switch (val) {
.basic => return true,
.x86_64 => if (target.cpu.arch == .x86_64) return true,
.aarch64 => if (target.cpu.arch == .aarch64) return true,
.arm => if (target.cpu.arch == .arm) return true,
.ppc => switch (target.cpu.arch) {
.powerpc, .powerpc64, .powerpc64le => return true,
else => {},
},
else => {
// Todo: handle other target predicates
},
}
}
return false;
}
pub fn defaultFpEvalMethod(target: std.Target) LangOpts.FPEvalMethod {
switch (target.cpu.arch) {
.x86, .x86_64 => {
if (target.ptrBitWidth() == 32 and target.os.tag == .netbsd) {
if (target.os.version_range.semver.min.order(.{ .major = 6, .minor = 99, .patch = 26 }) != .gt) {
// NETBSD <= 6.99.26 on 32-bit x86 defaults to double
return .double;
}
}
if (std.Target.x86.featureSetHas(target.cpu.features, .sse)) {
return .source;
}
return .extended;
},
else => {},
}
return .source;
}
/// Value of the `-m` flag for `ld` for this target
pub fn ldEmulationOption(target: std.Target, arm_endianness: ?std.builtin.Endian) ?[]const u8 {
return switch (target.cpu.arch) {
.x86 => "elf_i386",
.arm,
.armeb,
.thumb,
.thumbeb,
=> switch (arm_endianness orelse target.cpu.arch.endian()) {
.little => "armelf_linux_eabi",
.big => "armelfb_linux_eabi",
},
.aarch64 => "aarch64linux",
.aarch64_be => "aarch64linuxb",
.m68k => "m68kelf",
.powerpc => if (target.os.tag == .linux) "elf32ppclinux" else "elf32ppc",
.powerpcle => if (target.os.tag == .linux) "elf32lppclinux" else "elf32lppc",
.powerpc64 => "elf64ppc",
.powerpc64le => "elf64lppc",
.riscv32 => "elf32lriscv",
.riscv32be => "elf32briscv",
.riscv64 => "elf64lriscv",
.riscv64be => "elf64briscv",
.sparc => "elf32_sparc",
.sparc64 => "elf64_sparc",
.loongarch32 => "elf32loongarch",
.loongarch64 => "elf64loongarch",
.mips => "elf32btsmip",
.mipsel => "elf32ltsmip",
.mips64 => switch (target.abi) {
.gnuabin32, .muslabin32 => "elf32btsmipn32",
else => "elf64btsmip",
},
.mips64el => switch (target.abi) {
.gnuabin32, .muslabin32 => "elf32ltsmipn32",
else => "elf64ltsmip",
},
.x86_64 => switch (target.abi) {
.gnux32, .muslx32 => "elf32_x86_64",
else => "elf_x86_64",
},
.ve => "elf64ve",
.csky => "cskyelf_linux",
else => null,
};
}
pub fn get32BitArchVariant(target: std.Target) ?std.Target {
var copy = target;
switch (target.cpu.arch) {
.amdgcn,
.avr,
.msp430,
.ve,
.bpfel,
.bpfeb,
.kvx,
.s390x,
=> return null,
.arc,
.arm,
.armeb,
.csky,
.hexagon,
.m68k,
.mips,
.mipsel,
.powerpc,
.powerpcle,
.riscv32,
.riscv32be,
.sparc,
.thumb,
.thumbeb,
.x86,
.xcore,
.nvptx,
.kalimba,
.lanai,
.wasm32,
.spirv32,
.loongarch32,
.xtensa,
.propeller,
.or1k,
=> {}, // Already 32 bit
.aarch64 => copy.cpu.arch = .arm,
.aarch64_be => copy.cpu.arch = .armeb,
.nvptx64 => copy.cpu.arch = .nvptx,
.wasm64 => copy.cpu.arch = .wasm32,
.spirv64 => copy.cpu.arch = .spirv32,
.loongarch64 => copy.cpu.arch = .loongarch32,
.mips64 => copy.cpu.arch = .mips,
.mips64el => copy.cpu.arch = .mipsel,
.powerpc64 => copy.cpu.arch = .powerpc,
.powerpc64le => copy.cpu.arch = .powerpcle,
.riscv64 => copy.cpu.arch = .riscv32,
.riscv64be => copy.cpu.arch = .riscv32be,
.sparc64 => copy.cpu.arch = .sparc,
.x86_64 => copy.cpu.arch = .x86,
}
return copy;
}
pub fn get64BitArchVariant(target: std.Target) ?std.Target {
var copy = target;
switch (target.cpu.arch) {
.arc,
.avr,
.csky,
.hexagon,
.kalimba,
.lanai,
.m68k,
.msp430,
.xcore,
.xtensa,
.propeller,
.or1k,
=> return null,
.aarch64,
.aarch64_be,
.amdgcn,
.bpfeb,
.bpfel,
.nvptx64,
.wasm64,
.spirv64,
.kvx,
.loongarch64,
.mips64,
.mips64el,
.powerpc64,
.powerpc64le,
.riscv64,
.riscv64be,
.s390x,
.sparc64,
.ve,
.x86_64,
=> {}, // Already 64 bit
.arm => copy.cpu.arch = .aarch64,
.armeb => copy.cpu.arch = .aarch64_be,
.loongarch32 => copy.cpu.arch = .loongarch64,
.mips => copy.cpu.arch = .mips64,
.mipsel => copy.cpu.arch = .mips64el,
.nvptx => copy.cpu.arch = .nvptx64,
.powerpc => copy.cpu.arch = .powerpc64,
.powerpcle => copy.cpu.arch = .powerpc64le,
.riscv32 => copy.cpu.arch = .riscv64,
.riscv32be => copy.cpu.arch = .riscv64be,
.sparc => copy.cpu.arch = .sparc64,
.spirv32 => copy.cpu.arch = .spirv64,
.thumb => copy.cpu.arch = .aarch64,
.thumbeb => copy.cpu.arch = .aarch64_be,
.wasm32 => copy.cpu.arch = .wasm64,
.x86 => copy.cpu.arch = .x86_64,
}
return copy;
}
/// Adapted from Zig's src/codegen/llvm.zig
pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 {
// 64 bytes is assumed to be large enough to hold any target triple; increase if necessary
std.debug.assert(buf.len >= 64);
var writer: std.Io.Writer = .fixed(buf);
const llvm_arch = switch (target.cpu.arch) {
.arm => "arm",
.armeb => "armeb",
.aarch64 => if (target.abi == .ilp32) "aarch64_32" else "aarch64",
.aarch64_be => "aarch64_be",
.arc => "arc",
.avr => "avr",
.bpfel => "bpfel",
.bpfeb => "bpfeb",
.csky => "csky",
.hexagon => "hexagon",
.loongarch32 => "loongarch32",
.loongarch64 => "loongarch64",
.m68k => "m68k",
.mips => "mips",
.mipsel => "mipsel",
.mips64 => "mips64",
.mips64el => "mips64el",
.msp430 => "msp430",
.powerpc => "powerpc",
.powerpcle => "powerpcle",
.powerpc64 => "powerpc64",
.powerpc64le => "powerpc64le",
.amdgcn => "amdgcn",
.riscv32 => "riscv32",
.riscv32be => "riscv32be",
.riscv64 => "riscv64",
.riscv64be => "riscv64be",
.sparc => "sparc",
.sparc64 => "sparc64",
.s390x => "s390x",
.thumb => "thumb",
.thumbeb => "thumbeb",
.x86 => "i386",
.x86_64 => "x86_64",
.xcore => "xcore",
.xtensa => "xtensa",
.nvptx => "nvptx",
.nvptx64 => "nvptx64",
.spirv32 => "spirv32",
.spirv64 => "spirv64",
.lanai => "lanai",
.wasm32 => "wasm32",
.wasm64 => "wasm64",
.ve => "ve",
// Note: propeller1, kalimba, kvx, and or1k are not supported in LLVM; this is the Zig arch name
.kalimba => "kalimba",
.kvx => "kvx",
.propeller => "propeller",
.or1k => "or1k",
};
writer.writeAll(llvm_arch) catch unreachable;
writer.writeByte('-') catch unreachable;
const llvm_os = switch (target.os.tag) {
.freestanding => "unknown",
.dragonfly => "dragonfly",
.freebsd => "freebsd",
.fuchsia => "fuchsia",
.linux => "linux",
.ps3 => "lv2",
.netbsd => "netbsd",
.openbsd => "openbsd",
.illumos => "solaris",
.windows => "windows",
.haiku => "haiku",
.rtems => "rtems",
.cuda => "cuda",
.nvcl => "nvcl",
.amdhsa => "amdhsa",
.ps4 => "ps4",
.ps5 => "ps5",
.mesa3d => "mesa3d",
.contiki => "contiki",
.amdpal => "amdpal",
.hermit => "hermit",
.hurd => "hurd",
.wasi => "wasi",
.emscripten => "emscripten",
.uefi => "windows",
.macos => "macosx",
.ios, .maccatalyst => "ios",
.tvos => "tvos",
.watchos => "watchos",
.driverkit => "driverkit",
.visionos => "xros",
.serenity => "serenity",
.vulkan => "vulkan",
.managarm => "managarm",
.@"3ds",
.vita,
.opencl,
.opengl,
.plan9,
.other,
=> "unknown",
};
writer.writeAll(llvm_os) catch unreachable;
if (target.os.tag.isDarwin()) {
const min_version = target.os.version_range.semver.min;
writer.print("{d}.{d}.{d}", .{
min_version.major,
min_version.minor,
min_version.patch,
}) catch unreachable;
}
writer.writeByte('-') catch unreachable;
const llvm_abi = switch (target.abi) {
.none => if (target.os.tag == .maccatalyst) "macabi" else "unknown",
.ilp32 => "unknown",
.gnu => "gnu",
.gnuabin32 => "gnuabin32",
.gnuabi64 => "gnuabi64",
.gnueabi => "gnueabi",
.gnueabihf => "gnueabihf",
.gnuf32 => "gnuf32",
.gnusf => "gnusf",
.gnux32 => "gnux32",
.code16 => "code16",
.eabi => "eabi",
.eabihf => "eabihf",
.android => "android",
.androideabi => "androideabi",
.musl => "musl",
.muslabin32 => "muslabin32",
.muslabi64 => "muslabi64",
.musleabi => "musleabi",
.musleabihf => "musleabihf",
.muslf32 => "muslf32",
.muslsf => "muslsf",
.muslx32 => "muslx32",
.msvc => "msvc",
.itanium => "itanium",
.simulator => "simulator",
.ohos => "ohos",
.ohoseabi => "ohoseabi",
};
writer.writeAll(llvm_abi) catch unreachable;
return writer.buffered();
}
pub const DefaultPIStatus = enum { yes, no, depends_on_linker };
pub fn isPIEDefault(target: std.Target) DefaultPIStatus {
return switch (target.os.tag) {
.haiku,
.maccatalyst,
.macos,
.ios,
.tvos,
.watchos,
.visionos,
.driverkit,
.dragonfly,
.netbsd,
.freebsd,
.illumos,
.cuda,
.amdhsa,
.amdpal,
.mesa3d,
.ps4,
.ps5,
.hurd,
=> .no,
.openbsd,
.fuchsia,
=> .yes,
.linux => {
if (target.abi == .ohos)
return .yes;
switch (target.cpu.arch) {
.ve => return .no,
else => return if (target.os.tag == .linux or target.abi.isAndroid() or target.abi.isMusl()) .yes else .no,
}
},
.windows => {
if (target.isMinGW())
return .no;
if (target.abi == .itanium)
return if (target.cpu.arch == .x86_64) .yes else .no;
if (target.abi == .msvc or target.abi == .none)
return .depends_on_linker;
return .no;
},
else => {
switch (target.cpu.arch) {
.hexagon => {
// CLANG_DEFAULT_PIE_ON_LINUX
return if (target.os.tag == .linux or target.abi.isAndroid() or target.abi.isMusl()) .yes else .no;
},
else => return .no,
}
},
};
}
pub fn isPICdefault(target: std.Target) DefaultPIStatus {
return switch (target.os.tag) {
.haiku,
.maccatalyst,
.macos,
.ios,
.tvos,
.watchos,
.visionos,
.driverkit,
.amdhsa,
.amdpal,
.mesa3d,
.ps4,
.ps5,
=> .yes,
.fuchsia,
.cuda,
=> .no,
.dragonfly,
.openbsd,
.netbsd,
.freebsd,
.illumos,
.hurd,
=> {
return switch (target.cpu.arch) {
.mips64, .mips64el => .yes,
else => .no,
};
},
.linux => {
if (target.abi == .ohos)
return .no;
return switch (target.cpu.arch) {
.mips64, .mips64el => .yes,
else => .no,
};
},
.windows => {
if (target.isMinGW())
return if (target.cpu.arch == .x86_64 or target.cpu.arch == .aarch64) .yes else .no;
if (target.abi == .itanium)
return if (target.cpu.arch == .x86_64) .yes else .no;
if (target.abi == .msvc or target.abi == .none)
return .depends_on_linker;
if (target.ofmt == .macho)
return .yes;
return switch (target.cpu.arch) {
.x86_64, .mips64, .mips64el => .yes,
else => .no,
};
},
else => {
if (target.ofmt == .macho)
return .yes;
return switch (target.cpu.arch) {
.mips64, .mips64el => .yes,
else => .no,
};
},
};
}
pub fn isPICDefaultForced(target: std.Target) DefaultPIStatus {
return switch (target.os.tag) {
.amdhsa, .amdpal, .mesa3d => .yes,
.haiku,
.dragonfly,
.openbsd,
.netbsd,
.freebsd,
.illumos,
.cuda,
.ps4,
.ps5,
.hurd,
.linux,
.fuchsia,
=> .no,
.windows => {
if (target.isMinGW())
return .yes;
if (target.abi == .itanium)
return if (target.cpu.arch == .x86_64) .yes else .no;
// if (bfd) return target.cpu.arch == .x86_64 else target.cpu.arch == .x86_64 or target.cpu.arch == .aarch64;
if (target.abi == .msvc or target.abi == .none)
return .depends_on_linker;
if (target.ofmt == .macho)
return if (target.cpu.arch == .aarch64 or target.cpu.arch == .x86_64) .yes else .no;
return if (target.cpu.arch == .x86_64) .yes else .no;
},
.maccatalyst,
.macos,
.ios,
.tvos,
.watchos,
.visionos,
.driverkit,
=> if (target.cpu.arch == .x86_64 or target.cpu.arch == .aarch64) .yes else .no,
else => {
return switch (target.cpu.arch) {
.hexagon,
.lanai,
.avr,
.riscv32,
.riscv64,
.csky,
.xcore,
.wasm32,
.wasm64,
.ve,
.spirv32,
.spirv64,
=> .no,
.msp430 => .yes,
else => {
if (target.ofmt == .macho)
return if (target.cpu.arch == .aarch64 or target.cpu.arch == .x86_64) .yes else .no;
return .no;
},
};
},
};
}
test "alignment functions - smoke test" {
const linux: std.Target.Os = .{ .tag = .linux, .version_range = .{ .none = {} } };
const x86_64_target: std.Target = .{
.abi = std.Target.Abi.default(.x86_64, linux.tag),
.cpu = std.Target.Cpu.Model.generic(.x86_64).toCpu(.x86_64),
.os = linux,
.ofmt = .elf,
};
try std.testing.expect(isTlsSupported(x86_64_target));
try std.testing.expect(!ignoreNonZeroSizedBitfieldTypeAlignment(x86_64_target));
try std.testing.expect(minZeroWidthBitfieldAlignment(x86_64_target) == null);
try std.testing.expect(!unnamedFieldAffectsAlignment(x86_64_target));
try std.testing.expect(defaultAlignment(x86_64_target) == 16);
try std.testing.expect(!packAllEnums(x86_64_target));
try std.testing.expect(systemCompiler(x86_64_target) == .gcc);
}
test "target size/align tests" {
var comp: @import("Compilation.zig") = undefined;
const linux: std.Target.Os = .{ .tag = .linux, .version_range = .{ .none = {} } };
const x86_target: std.Target = .{
.abi = std.Target.Abi.default(.x86, linux.tag),
.cpu = std.Target.Cpu.Model.generic(.x86).toCpu(.x86),
.os = linux,
.ofmt = .elf,
};
comp.target = x86_target;
const tt: QualType = .long_long;
try std.testing.expectEqual(@as(u64, 8), tt.sizeof(&comp));
try std.testing.expectEqual(@as(u64, 4), tt.alignof(&comp));
}
/// The canonical integer representation of nullptr_t.
pub fn nullRepr(_: std.Target) u64 {
return 0;
}

View File

@ -167,7 +167,7 @@ fn genDecls(c: *AsmCodeGen) !void {
if (c.tree.comp.code_gen_options.debug != .strip) {
const sources = c.tree.comp.sources.values();
for (sources) |source| {
try c.data.print(" .file {d} \"{s}\"\n", .{ @intFromEnum(source.id) - 1, source.path });
try c.data.print(" .file {d} \"{s}\"\n", .{ @intFromEnum(source.id.index) + 1, source.path });
}
}

View File

@ -12,9 +12,8 @@ pub fn deinit(self: *const Assembly, gpa: Allocator) void {
}
pub fn writeToFile(self: Assembly, file: std.fs.File) !void {
var vec: [2]std.posix.iovec_const = .{
.{ .base = self.data.ptr, .len = self.data.len },
.{ .base = self.text.ptr, .len = self.text.len },
};
return file.writevAll(&vec);
var file_writer = file.writer(&.{});
var buffers = [_][]const u8{ self.data, self.text };
try file_writer.interface.writeSplatAll(&buffers, 1);
}

View File

@ -9,21 +9,31 @@ const Driver = aro.Driver;
const Toolchain = aro.Toolchain;
const assembly_backend = @import("assembly_backend");
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
var debug_allocator: std.heap.DebugAllocator(.{
.stack_trace_frames = 0,
// A unique value so that when a default-constructed
// GeneralPurposeAllocator is incorrectly passed to testing allocator, or
// vice versa, panic occurs.
.canary = @truncate(0xc647026dc6875134),
}) = .{};
pub fn main() u8 {
const gpa = if (@import("builtin").link_libc)
std.heap.raw_c_allocator
else
general_purpose_allocator.allocator();
debug_allocator.allocator();
defer if (!@import("builtin").link_libc) {
_ = general_purpose_allocator.deinit();
_ = debug_allocator.deinit();
};
var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = arena_instance.allocator();
var threaded: std.Io.Threaded = .init(gpa);
defer threaded.deinit();
const io = threaded.io();
const fast_exit = @import("builtin").mode != .Debug;
const args = process.argsAlloc(arena) catch {
@ -48,7 +58,7 @@ pub fn main() u8 {
} },
};
var comp = Compilation.initDefault(gpa, arena, &diagnostics, std.fs.cwd()) catch |er| switch (er) {
var comp = Compilation.initDefault(gpa, arena, io, &diagnostics, std.fs.cwd()) catch |er| switch (er) {
error.OutOfMemory => {
std.debug.print("out of memory\n", .{});
if (fast_exit) process.exit(1);
@ -60,7 +70,7 @@ pub fn main() u8 {
var driver: Driver = .{ .comp = &comp, .aro_name = aro_name, .diagnostics = &diagnostics };
defer driver.deinit();
var toolchain: Toolchain = .{ .driver = &driver, .filesystem = .{ .real = comp.cwd } };
var toolchain: Toolchain = .{ .driver = &driver };
defer toolchain.deinit();
driver.main(&toolchain, args, fast_exit, assembly_backend.genAsm) catch |er| switch (er) {

View File

@ -29,6 +29,7 @@ pub fn preprocess(
error.OutOfMemory => |e| return e,
error.WriteFailed => unreachable,
};
try comp.initSearchPath(driver.includes.items, false);
if (hasAnyErrors(comp)) return error.ArgError;
@ -59,7 +60,7 @@ pub fn preprocess(
pp.preserve_whitespace = true;
pp.linemarkers = .line_directives;
pp.preprocessSources(&.{ source, builtin_macros, user_macros }) catch |err| switch (err) {
pp.preprocessSources(.{ .main = source, .builtin = builtin_macros, .command_line = user_macros }) catch |err| switch (err) {
error.FatalError => return error.PreprocessError,
else => |e| return e,
};
@ -71,7 +72,7 @@ pub fn preprocess(
if (maybe_dependencies) |dependencies| {
for (comp.sources.values()) |comp_source| {
if (comp_source.id == builtin_macros.id or comp_source.id == user_macros.id) continue;
if (comp_source.id == .unused or comp_source.id == .generated) continue;
if (comp_source.id.index == .unused or comp_source.id.index == .generated) continue;
const duped_path = try dependencies.allocator.dupe(u8, comp_source.path);
errdefer dependencies.allocator.free(duped_path);
try dependencies.list.append(dependencies.allocator, duped_path);

View File

@ -79,6 +79,20 @@ pub fn transFnMacro(mt: *MacroTranslator) ParseError!void {
try block_scope.discardVariable(mangled_name);
}
// #define FOO(x)
if (mt.peek() == .eof) {
try block_scope.statements.append(mt.t.gpa, ZigTag.return_void.init());
const fn_decl = try ZigTag.pub_inline_fn.create(mt.t.arena, .{
.name = mt.name,
.params = fn_params,
.return_type = ZigTag.void_type.init(),
.body = try block_scope.complete(),
});
try mt.t.addTopLevelDecl(mt.name, fn_decl);
return;
}
const expr = try mt.parseCExpr(scope);
const last = mt.peek();
if (last != .eof)
@ -252,7 +266,7 @@ fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
const lit_bytes = mt.tokSlice();
mt.i += 1;
var bytes = try std.ArrayListUnmanaged(u8).initCapacity(arena, lit_bytes.len + 3);
var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3);
const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
switch (prefix) {
@ -637,7 +651,7 @@ fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
// for handling type macros (EVIL)
// TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
if (try mt.parseCTypeName(scope, true)) |type_name| {
if (try mt.parseCTypeName(scope)) |type_name| {
return type_name;
}
@ -825,6 +839,18 @@ fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
switch (mt.peek()) {
.asterisk => {
mt.i += 1;
switch (mt.peek()) {
.comma, .r_paren, .eof => {
// This is probably a pointer type
return ZigTag.c_pointer.create(mt.t.arena, .{
.is_const = false,
.is_volatile = false,
.is_allowzero = false,
.elem_type = node,
});
},
else => {},
}
const lhs = try mt.macroIntFromBool(node);
const rhs = try mt.macroIntFromBool(try mt.parseCCastExpr(scope));
node = try ZigTag.mul.create(mt.t.arena, .{ .lhs = lhs, .rhs = rhs });
@ -848,7 +874,7 @@ fn parseCMulExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
if (mt.eat(.l_paren)) {
if (try mt.parseCTypeName(scope, true)) |type_name| {
if (try mt.parseCTypeName(scope)) |type_name| {
while (true) {
const next_tok = mt.peek();
if (next_tok == .r_paren) {
@ -882,14 +908,14 @@ fn parseCCastExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
}
// allow_fail is set when unsure if we are parsing a type-name
fn parseCTypeName(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
if (try mt.parseCSpecifierQualifierList(scope, allow_fail)) |node| {
fn parseCTypeName(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
if (try mt.parseCSpecifierQualifierList(scope)) |node| {
return try mt.parseCAbstractDeclarator(node);
}
return null;
}
fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail: bool) ParseError!?ZigNode {
fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope) ParseError!?ZigNode {
const tok = mt.peek();
switch (tok) {
.macro_param, .macro_param_no_expand => {
@ -897,9 +923,9 @@ fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail:
// Assume that this is only a cast if the next token is ')'
// e.g. param)identifier
if (allow_fail and (mt.macro.tokens.len < mt.i + 3 or
if (mt.macro.tokens.len < mt.i + 3 or
mt.macro.tokens[mt.i + 1].id != .r_paren or
mt.macro.tokens[mt.i + 2].id != .identifier))
mt.macro.tokens[mt.i + 2].id != .identifier)
return null;
mt.i += 1;
@ -912,10 +938,10 @@ fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail:
if (mt.t.global_scope.blank_macros.contains(slice)) {
mt.i += 1;
return try mt.parseCSpecifierQualifierList(scope, allow_fail);
return try mt.parseCSpecifierQualifierList(scope);
}
if (!allow_fail or mt.t.typedefs.contains(mangled_name)) {
if (mt.t.typedefs.contains(mangled_name)) {
mt.i += 1;
if (Translator.builtin_typedef_map.get(mangled_name)) |ty| {
return try ZigTag.type.create(mt.t.arena, ty);
@ -952,21 +978,27 @@ fn parseCSpecifierQualifierList(mt: *MacroTranslator, scope: *Scope, allow_fail:
.keyword_enum, .keyword_struct, .keyword_union => {
const tag_name = mt.tokSlice();
mt.i += 1;
if (mt.peek() != .identifier) {
mt.i -= 1;
return null;
}
// struct Foo will be declared as struct_Foo by transRecordDecl
const identifier = mt.tokSlice();
try mt.expect(.identifier);
const name = try std.fmt.allocPrint(mt.t.arena, "{s}_{s}", .{ tag_name, identifier });
if (!mt.t.global_scope.contains(name)) {
try mt.fail("unable to translate C expr: '{s}' not found", .{name});
return error.ParseError;
}
return try ZigTag.identifier.create(mt.t.arena, name);
},
else => {},
}
if (allow_fail) return null;
try mt.fail("unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
return error.ParseError;
return null;
}
fn parseCNumericType(mt: *MacroTranslator) ParseError!ZigNode {
@ -1126,6 +1158,11 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo
switch (mt.peek()) {
.period => {
mt.i += 1;
const tok = mt.tokens[mt.i];
if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
try mt.fail("unable to translate C expr: field access using macro parameter", .{});
return error.ParseError;
}
const field_name = mt.tokSlice();
try mt.expect(.identifier);
@ -1133,6 +1170,11 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo
},
.arrow => {
mt.i += 1;
const tok = mt.tokens[mt.i];
if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
try mt.fail("unable to translate C expr: field access using macro parameter", .{});
return error.ParseError;
}
const field_name = mt.tokSlice();
try mt.expect(.identifier);
@ -1286,7 +1328,7 @@ fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
.keyword_sizeof => {
mt.i += 1;
const operand = if (mt.eat(.l_paren)) blk: {
const inner = (try mt.parseCTypeName(scope, false)).?;
const inner = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
try mt.expect(.r_paren);
break :blk inner;
} else try mt.parseCUnaryExpr(scope);
@ -1298,7 +1340,7 @@ fn parseCUnaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
// TODO this won't work if using <stdalign.h>'s
// #define alignof _Alignof
try mt.expect(.l_paren);
const operand = (try mt.parseCTypeName(scope, false)).?;
const operand = (try mt.parseCTypeName(scope)) orelse try mt.parseCUnaryExpr(scope);
try mt.expect(.r_paren);
return ZigTag.alignof.create(mt.t.arena, operand);

View File

@ -178,6 +178,7 @@ fn tokenizeMacro(allocator: mem.Allocator, source: []const u8, tok_list: *std.Ar
.buf = source,
.source = .unused,
.langopts = .{},
.splice_locs = &.{},
};
{
const name_tok = tokenizer.nextNoWS();

View File

@ -8,7 +8,7 @@ const Translator = @import("Translator.zig");
const Scope = @This();
pub const SymbolTable = std.StringArrayHashMapUnmanaged(ast.Node);
pub const AliasList = std.ArrayListUnmanaged(struct {
pub const AliasList = std.ArrayList(struct {
alias: []const u8,
name: []const u8,
});
@ -16,7 +16,7 @@ pub const AliasList = std.ArrayListUnmanaged(struct {
/// Associates a container (structure or union) with its relevant member functions.
pub const ContainerMemberFns = struct {
container_decl_ptr: *ast.Node,
member_fns: std.ArrayListUnmanaged(*ast.Payload.Func) = .empty,
member_fns: std.ArrayList(*ast.Payload.Func) = .empty,
};
pub const ContainerMemberFnsHashMap = std.AutoArrayHashMapUnmanaged(aro.QualType, ContainerMemberFns);
@ -55,7 +55,7 @@ pub const Condition = struct {
pub const Block = struct {
base: Scope,
translator: *Translator,
statements: std.ArrayListUnmanaged(ast.Node),
statements: std.ArrayList(ast.Node),
variables: AliasList,
mangle_count: u32 = 0,
label: ?[]const u8 = null,
@ -195,7 +195,7 @@ pub const Root = struct {
translator: *Translator,
sym_table: SymbolTable,
blank_macros: std.StringArrayHashMapUnmanaged(void),
nodes: std.ArrayListUnmanaged(ast.Node),
nodes: std.ArrayList(ast.Node),
container_member_fns_map: ContainerMemberFnsHashMap,
pub fn init(t: *Translator) Root {
@ -252,7 +252,7 @@ pub const Root = struct {
const gpa = root.translator.gpa;
const arena = root.translator.arena;
var member_names: std.StringArrayHashMapUnmanaged(u32) = .empty;
var member_names: std.StringArrayHashMapUnmanaged(void) = .empty;
defer member_names.deinit(gpa);
for (root.container_member_fns_map.values()) |members| {
member_names.clearRetainingCapacity();
@ -261,7 +261,7 @@ pub const Root = struct {
const payload: *ast.Payload.Container = @alignCast(@fieldParentPtr("base", members.container_decl_ptr.ptr_otherwise));
// Avoid duplication with field names
for (payload.data.fields) |field| {
try member_names.put(gpa, field.name, 0);
try member_names.put(gpa, field.name, {});
}
break :blk_record &payload.data.decls;
},
@ -278,34 +278,39 @@ pub const Root = struct {
};
const old_decls = decls_ptr.*;
const new_decls = try arena.alloc(ast.Node, old_decls.len + members.member_fns.items.len);
const new_decls = try arena.alloc(ast.Node, old_decls.len + members.member_fns.items.len * 2);
@memcpy(new_decls[0..old_decls.len], old_decls);
// Assume the allocator of payload.data.decls is arena,
// so don't add arena.free(old_variables).
const func_ref_vars = new_decls[old_decls.len..];
var count: u32 = 0;
// Add members without mangling them - only fields may cause name conflicts
for (members.member_fns.items) |func| {
const func_name = func.data.name.?;
const last_index = std.mem.lastIndexOf(u8, func_name, "_");
const last_name = if (last_index) |index| func_name[index + 1 ..] else continue;
var same_count: u32 = 0;
const gop = try member_names.getOrPutValue(gpa, last_name, same_count);
if (gop.found_existing) {
gop.value_ptr.* += 1;
same_count = gop.value_ptr.*;
}
const var_name = if (same_count == 0)
last_name
else
try std.fmt.allocPrint(arena, "{s}{d}", .{ last_name, same_count });
const member_name_slot = try member_names.getOrPutValue(gpa, func_name, {});
if (member_name_slot.found_existing) continue;
func_ref_vars[count] = try ast.Node.Tag.pub_var_simple.create(arena, .{
.name = var_name,
.init = try ast.Node.Tag.identifier.create(arena, func_name),
.name = func_name,
.init = try ast.Node.Tag.root_ref.create(arena, func_name),
});
count += 1;
}
for (members.member_fns.items) |func| {
const func_name = func.data.name.?;
const func_name_trimmed = std.mem.trimEnd(u8, func_name, "_");
const last_idx = std.mem.findLast(u8, func_name_trimmed, "_") orelse continue;
const func_name_alias = func_name[last_idx + 1 ..];
const member_name_slot = try member_names.getOrPutValue(gpa, func_name_alias, {});
if (member_name_slot.found_existing) continue;
func_ref_vars[count] = try ast.Node.Tag.pub_var_simple.create(arena, .{
.name = func_name_alias,
.init = try ast.Node.Tag.root_ref.create(arena, func_name),
});
count += 1;
}
decls_ptr.* = new_decls[0 .. old_decls.len + count];
}
}

View File

@ -82,12 +82,11 @@ pub fn getMangle(t: *Translator) u32 {
/// Convert an `aro.Source.Location` to a 'file:line:column' string.
pub fn locStr(t: *Translator, loc: aro.Source.Location) ![]const u8 {
const source = t.comp.getSource(loc.id);
const line_col = source.lineCol(loc);
const filename = source.path;
const expanded = loc.expand(t.comp);
const filename = expanded.path;
const line = source.physicalLine(loc);
const col = line_col.col;
const line = expanded.line_no;
const col = expanded.col;
return std.fmt.allocPrint(t.arena, "{s}:{d}:{d}", .{ filename, line, col });
}
@ -139,7 +138,11 @@ pub fn failDeclExtra(
// location
// pub const name = @compileError(msg);
const fail_msg = try std.fmt.allocPrint(t.arena, format, args);
const fail_decl = try ZigTag.fail_decl.create(t.arena, .{ .actual = name, .mangled = fail_msg });
const fail_decl = try ZigTag.fail_decl.create(t.arena, .{
.actual = name,
.mangled = fail_msg,
.local = scope.id != .root,
});
const str = try t.locStr(loc);
const location_comment = try std.fmt.allocPrint(t.arena, "// {s}", .{str});
@ -220,6 +223,7 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {
defer allocating.deinit();
allocating.writer.writeAll(
\\const __root = @This();
\\pub const __builtin = @import("std").zig.c_translation.builtins;
\\pub const __helpers = @import("std").zig.c_translation.helpers;
\\
@ -297,7 +301,7 @@ fn prepopulateGlobalNameTable(t: *Translator) !void {
}
for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
if (macro.is_builtin) continue;
if (macro.isBuiltin()) continue;
if (!t.isSelfDefinedMacro(name, macro)) {
try t.global_names.put(t.gpa, name, {});
}
@ -527,6 +531,13 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
break :init ZigTag.opaque_literal.init();
}
// Demote record to opaque if it contains an opaque field
if (t.typeWasDemotedToOpaque(field.qt)) {
try t.opaque_demotes.put(t.gpa, base.qt, {});
try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
break :init ZigTag.opaque_literal.init();
}
var field_name = field.name.lookup(t.comp);
if (field.name_tok == 0) {
field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
@ -856,12 +867,18 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
break :blk null;
};
// TODO actually set with @export/@extern
const linkage = variable.qt.linkage(t.comp);
if (linkage != .strong) {
try t.warn(scope, variable.name_tok, "TODO {s} linkage ignored", .{@tagName(linkage)});
}
const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
var node = try ZigTag.var_decl.create(t.arena, .{
.is_pub = toplevel,
.is_const = is_const,
.is_extern = is_extern,
.is_export = toplevel and variable.storage_class == .auto,
.is_export = toplevel and variable.storage_class == .auto and linkage == .strong,
.is_threadlocal = variable.thread_local,
.linksection_string = linksection_string,
.alignment = alignment,
@ -1013,7 +1030,7 @@ fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAs
try scope.appendNode(assert_node);
}
fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.SimpleAsm) Error!void {
fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.GlobalAsm) Error!void {
const asm_string = t.tree.value_map.get(global_asm.asm_str).?;
const bytes = t.comp.interner.get(asm_string.ref()).bytes;
@ -1071,6 +1088,17 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex
.double => return ZigTag.type.create(t.arena, "f64"),
.long_double => return ZigTag.type.create(t.arena, "c_longdouble"),
.float128 => return ZigTag.type.create(t.arena, "f128"),
.bf16,
.float32,
.float64,
.float32x,
.float64x,
.float128x,
.dfloat32,
.dfloat64,
.dfloat128,
.dfloat64x,
=> return t.fail(error.UnsupportedType, source_loc, "TODO support float type: '{s}'", .{try t.getTypeStr(qt)}),
},
.pointer => |pointer_ty| {
const child_qt = pointer_ty.child;
@ -1165,17 +1193,8 @@ fn headFieldAlignment(t: *Translator, record_decl: aro.Type.Record) ?c_uint {
const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
var max_field_alignment_bits: u64 = 0;
for (record_decl.fields) |field| {
if (field.qt.getRecord(t.comp)) |field_record_decl| {
const child_record_alignment = field_record_decl.layout.?.field_alignment_bits;
if (child_record_alignment > max_field_alignment_bits)
max_field_alignment_bits = child_record_alignment;
} else {
const field_size = field.layout.size_bits;
if (field_size > max_field_alignment_bits)
max_field_alignment_bits = field_size;
}
}
for (record_decl.fields) |field|
max_field_alignment_bits = @max(max_field_alignment_bits, bits_per_byte * field.qt.alignof(t.comp));
if (max_field_alignment_bits != parent_ptr_alignment_bits) {
return parent_ptr_alignment;
} else {
@ -1227,10 +1246,7 @@ fn alignmentForField(
// Records have a natural alignment when used as a field, and their size is
// a multiple of this alignment value. For all other types, the natural alignment
// is their size.
const field_natural_alignment_bits: u64 = if (field.qt.getRecord(t.comp)) |record|
record.layout.?.field_alignment_bits
else
field_size_bits;
const field_natural_alignment_bits: u64 = bits_per_byte * field.qt.alignof(t.comp);
const rem_bits = field_offset_bits % field_natural_alignment_bits;
// If there's a remainder, then the alignment is smaller than the field's
@ -1351,13 +1367,19 @@ fn transFnType(
}
};
// TODO actually set with @export/@extern
const linkage = func_qt.linkage(t.comp);
if (linkage != .strong) {
try t.warn(scope, source_loc, "TODO {s} linkage ignored", .{@tagName(linkage)});
}
const payload = try t.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_export = ctx.is_export and linkage == .strong,
.is_inline = ctx.is_always_inline,
.is_var_args = switch (func_ty.kind) {
.normal => false,
@ -1446,18 +1468,7 @@ fn typeIsOpaque(t: *Translator, qt: QualType) bool {
}
fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {
const base = qt.base(t.comp);
switch (base.type) {
.@"struct", .@"union" => |record_ty| {
if (t.opaque_demotes.contains(base.qt)) return true;
for (record_ty.fields) |field| {
if (t.typeWasDemotedToOpaque(field.qt)) return true;
}
return false;
},
.@"enum" => return t.opaque_demotes.contains(base.qt),
else => return false,
}
return t.opaque_demotes.contains(qt);
}
fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {
@ -1539,6 +1550,9 @@ fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode
.goto_stmt, .computed_goto_stmt, .labeled_stmt => {
return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO goto", .{});
},
.asm_stmt => {
return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO asm stmt", .{});
},
else => return t.transExprCoercing(scope, stmt, .unused),
}
}
@ -2197,7 +2211,7 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)
.default_stmt,
.goto_stmt,
.computed_goto_stmt,
.gnu_asm_simple,
.asm_stmt,
.global_asm,
.typedef,
.struct_decl,
@ -3031,6 +3045,10 @@ fn transMemberAccess(
.normal => member_access.base.qt(t.tree),
.ptr => member_access.base.qt(t.tree).childType(t.comp),
};
if (t.typeWasDemotedToOpaque(base_info)) {
return t.fail(error.UnsupportedTranslation, member_access.access_tok, "member access of demoted record", .{});
}
const record = base_info.getRecord(t.comp).?;
const field = record.fields[member_access.member_index];
const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
@ -3551,7 +3569,7 @@ fn transArrayInit(
const array_item_qt = array_init.container_qt.childType(t.comp);
const array_item_type = try t.transType(scope, array_item_qt, array_init.l_brace_tok);
var maybe_lhs: ?ZigNode = null;
var val_list: std.ArrayListUnmanaged(ZigNode) = .empty;
var val_list: std.ArrayList(ZigNode) = .empty;
defer val_list.deinit(t.gpa);
var i: usize = 0;
while (i < array_init.items.len) {
@ -3671,6 +3689,10 @@ fn transTypeInfo(
const operand = operand: {
if (typeinfo.expr) |expr| {
const operand = try t.transExpr(scope, expr, .used);
if (operand.tag() == .string_literal) {
const deref = try ZigTag.deref.create(t.arena, operand);
break :operand try ZigTag.typeof.create(t.arena, deref);
}
break :operand try ZigTag.typeof.create(t.arena, operand);
}
break :operand try t.transType(scope, typeinfo.operand_qt, typeinfo.op_tok);
@ -3962,7 +3984,8 @@ fn createFlexibleMemberFn(
// return @ptrCast(&self.*.<field_name>);
const address_of = try ZigTag.address_of.create(t.arena, field_access);
const casted = try ZigTag.ptr_cast.create(t.arena, address_of);
const aligned = try ZigTag.align_cast.create(t.arena, address_of);
const casted = try ZigTag.ptr_cast.create(t.arena, aligned);
const return_stmt = try ZigTag.@"return".create(t.arena, casted);
const body = try ZigTag.block_single.create(t.arena, return_stmt);
@ -3994,7 +4017,7 @@ fn transMacros(t: *Translator) !void {
defer pattern_list.deinit(t.gpa);
for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
if (macro.is_builtin) continue;
if (macro.isBuiltin()) continue;
if (t.global_scope.containsNow(name)) {
continue;
}

View File

@ -247,6 +247,9 @@ pub const Node = extern union {
/// comptime { if (!(lhs)) @compileError(rhs); }
static_assert,
/// __root.<name>
root_ref,
pub const last_no_payload_tag = Tag.@"break";
pub const no_payload_count = @intFromEnum(last_no_payload_tag) + 1;
@ -394,7 +397,8 @@ pub const Node = extern union {
.block => Payload.Block,
.c_pointer, .single_pointer => Payload.Pointer,
.array_type, .null_sentinel_array_type => Payload.Array,
.arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
.arg_redecl, .alias => Payload.ArgRedecl,
.fail_decl => Payload.FailDecl,
.var_simple, .pub_var_simple, .wrapped_local, .mut_str => Payload.SimpleVarDecl,
.enum_constant => Payload.EnumConstant,
.array_filler => Payload.ArrayFiller,
@ -405,6 +409,7 @@ pub const Node = extern union {
.builtin_extern => Payload.Extern,
.helper_call => Payload.HelperCall,
.helper_ref => Payload.HelperRef,
.root_ref => Payload.RootRef,
};
}
@ -708,6 +713,15 @@ pub const Payload = struct {
},
};
pub const FailDecl = struct {
base: Payload,
data: struct {
actual: []const u8,
mangled: []const u8,
local: bool,
},
};
pub const SimpleVarDecl = struct {
base: Payload,
data: struct {
@ -791,6 +805,11 @@ pub const Payload = struct {
base: Payload,
data: []const u8,
};
pub const RootRef = struct {
base: Payload,
data: []const u8,
};
};
/// Converts the nodes into a Zig Ast.
@ -860,7 +879,7 @@ const Context = struct {
gpa: Allocator,
buf: std.ArrayList(u8) = .empty,
nodes: std.zig.Ast.NodeList = .empty,
extra_data: std.ArrayListUnmanaged(u32) = .empty,
extra_data: std.ArrayList(u32) = .empty,
tokens: std.zig.Ast.TokenList = .empty,
fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
@ -1203,12 +1222,25 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
.fail_decl => {
const payload = node.castTag(.fail_decl).?.data;
// pub const name = @compileError(msg);
_ = try c.addToken(.keyword_pub, "pub");
// pub const name = (if (true))? @compileError(msg);
if (!payload.local) _ = try c.addToken(.keyword_pub, "pub");
const const_tok = try c.addToken(.keyword_const, "const");
_ = try c.addIdentifier(payload.actual);
_ = try c.addToken(.equal, "=");
var if_tok: TokenIndex = undefined;
var true_node: NodeIndex = undefined;
if (payload.local) {
if_tok = try c.addToken(.keyword_if, "if");
_ = try c.addToken(.l_paren, "(");
true_node = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addToken(.identifier, "true"),
.data = undefined,
});
_ = try c.addToken(.r_paren, ")");
}
const compile_error_tok = try c.addToken(.builtin, "@compileError");
_ = try c.addToken(.l_paren, "(");
const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{f}\"", .{std.zig.fmtString(payload.mangled)});
@ -1233,7 +1265,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
.data = .{
.opt_node_and_opt_node = .{
.none, // Type expression
compile_error.toOptional(), // Init expression
if (payload.local) // Init expression
(try c.addNode(.{
.tag = .if_simple,
.main_token = if_tok,
.data = .{ .node_and_node = .{
true_node, compile_error,
} },
})).toOptional()
else
compile_error.toOptional(),
},
},
});
@ -2158,6 +2199,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
});
},
.@"anytype" => unreachable, // Handled in renderParams
.root_ref => {
const payload = node.castTag(.root_ref).?.data;
const root_tok = try c.addNode(.{
.tag = .identifier,
.main_token = try c.addIdentifier("__root"),
.data = undefined,
});
return renderFieldAccess(c, root_tok, payload);
},
}
}
@ -2480,6 +2530,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.sqrt,
.trunc,
.floor,
.root_ref,
=> {
// no grouping needed
return renderNode(c, node);

View File

@ -22,7 +22,7 @@ pub fn main() u8 {
defer threaded.deinit();
const io = threaded.io();
var args = process.argsAlloc(arena) catch {
const args = process.argsAlloc(arena) catch {
std.debug.print("ran out of memory allocating arguments\n", .{});
if (fast_exit) process.exit(1);
return 1;
@ -58,7 +58,7 @@ pub fn main() u8 {
var driver: aro.Driver = .{ .comp = &comp, .diagnostics = &diagnostics, .aro_name = "aro" };
defer driver.deinit();
var toolchain: aro.Toolchain = .{ .driver = &driver, .filesystem = .{ .real = comp.cwd } };
var toolchain: aro.Toolchain = .{ .driver = &driver };
defer toolchain.deinit();
translate(&driver, &toolchain, args, zig_integration) catch |err| switch (err) {
@ -149,7 +149,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration
break :args args[0..i];
};
const user_macros = macros: {
var macro_buf: std.ArrayListUnmanaged(u8) = .empty;
var macro_buf: std.ArrayList(u8) = .empty;
defer macro_buf.deinit(gpa);
var discard_buf: [256]u8 = undefined;
@ -182,12 +182,10 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration
error.OutOfMemory => return error.OutOfMemory,
error.TooManyMultilibs => return d.fatal("found more than one multilib with the same priority", .{}),
};
tc.defineSystemIncludes() catch |er| switch (er) {
error.OutOfMemory => return error.OutOfMemory,
error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}),
};
try tc.defineSystemIncludes();
try d.comp.initSearchPath(d.includes.items, d.verbose_search_path);
const builtin_macros = d.comp.generateBuiltinMacros(.include_system_defines) catch |err| switch (err) {
const builtin_macros = d.comp.generateBuiltinMacros(d.system_defines) catch |err| switch (err) {
error.FileTooBig => return d.fatal("builtin macro source exceeded max size", .{}),
else => |e| return e,
};
@ -205,7 +203,13 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration
if (opt_dep_file) |*dep_file| pp.dep_file = dep_file;
try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
try pp.preprocessSources(.{
.main = source,
.builtin = builtin_macros,
.command_line = user_macros,
.imacros = d.imacros.items,
.implicit_includes = d.implicit_includes.items,
});
var c_tree = try pp.parse();
defer c_tree.deinit();

View File

@ -5693,7 +5693,7 @@ pub fn translateC(
const plus_or_minus = "-+"[@intFromBool(is_enabled)];
try buf.print(gpa, "{c}{s}", .{ plus_or_minus, feature.name });
}
break :mcpu try buf.toOwnedSlice(arena);
break :mcpu try arena.dupe(u8, buf.items);
};
try argv.append(mcpu);

View File

@ -307,7 +307,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
var aro_comp = aro.Compilation.init(gpa, arena, io, &diagnostics, std.fs.cwd());
defer aro_comp.deinit();
aro_comp.target = target.*;
aro_comp.target = .fromZigTarget(target.*);
const include_dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "mingw", "def-include" });
@ -318,7 +318,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
nosuspend stderr.print("include dir: {s}\n", .{include_dir}) catch break :print;
}
try aro_comp.include_dirs.append(gpa, include_dir);
try aro_comp.search_path.append(gpa, .{ .path = include_dir, .kind = .normal });
const builtin_macros = try aro_comp.generateBuiltinMacros(.include_system_defines);
const def_file_source = try aro_comp.addSourceFromPath(def_file_path);
@ -328,7 +328,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
pp.linemarkers = .none;
pp.preserve_whitespace = true;
try pp.preprocessSources(&.{ def_file_source, builtin_macros });
try pp.preprocessSources(.{ .main = def_file_source, .builtin = builtin_macros });
if (aro_comp.diagnostics.output.to_list.messages.items.len != 0) {
var buffer: [64]u8 = undefined;