Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for macro translations #12

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,72 @@ pub fn build(b: *std.Build) !void {
test_step.dependOn(unit_test_step);
}

const translate_exes = blk: {
// TODO is there a better way to do this?
const install_dir_step = b.addInstallDirectory(.{
.source_dir = aro.path("include"),
.install_dir = .prefix,
.install_subdir = "../.zig-cache/include",
});

var exes: [4]*std.Build.Step.Compile = undefined;
for (optimization_modes, 0..) |mode, i| {
const test_aro = b.dependency("aro", .{
.target = target,
.optimize = mode,
});
const test_exe = b.addExecutable(.{
.name = "translate-c",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = mode,
.use_llvm = use_llvm,
.use_lld = use_llvm,
});
test_exe.root_module.addImport("aro", test_aro.module("aro"));
test_exe.step.dependOn(&install_dir_step.step);

// Ensure that a binary is emitted.
_ = test_exe.getEmittedBin();
exes[i] = test_exe;
}
break :blk exes[0..optimization_modes.len];
};

{
const macro_test_step = b.step("test-macros", "Run unit tests");
for (optimization_modes, translate_exes) |mode, translate_exe| {
const macro_tests = b.addTest(.{
.root_source_file = b.path("test/macros.zig"),
.target = target,
.optimize = mode,
});
macro_tests.root_module.addImport("macros.h", TranslateC.create(b, .{
.root_source_file = b.path("test/macros.h"),
.target = target,
.optimize = mode,
.translate_c_exe = translate_exe,
}).createModule());
macro_tests.root_module.addImport("macros_not_utf8.h", TranslateC.create(b, .{
.root_source_file = b.path("test/macros_not_utf8.h"),
.target = target,
.optimize = mode,
.translate_c_exe = translate_exe,
}).createModule());

const run_macro_tests = b.addRunArtifact(macro_tests);
macro_test_step.dependOn(&run_macro_tests.step);
macro_test_step.dependOn(&translate_exe.step);
}
test_step.dependOn(macro_test_step);
}

try @import("test/cases.zig").addCaseTests(
b,
test_step,
optimization_modes,
translate_exes,
target,
skip_translate,
skip_run_translated,
use_llvm,
);
}
6 changes: 3 additions & 3 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.{
.name = "translate-c",
.version = "0.14.0-dev.2179+f2b81f57f",
.version = "0.0.0",
.dependencies = .{
.aro = .{
.url = "git+https://github.com/Vexu/arocc.git#2d6a1daa1a244af750c364fe3cc0845a4e2feb35",
.hash = "1220d973f5c528ba15fa67cebd60be721c79b8cfb0f9e8f99fb7526b8f9af3e160c6",
.url = "git+https://github.com/Vexu/arocc#1f3d0646b6d970a591d9c29e895040f3a9ab8899",
.hash = "1220c1c35f44b3c4fa1195b91aa2df0e23193a23e7801992ba2a1bd5a7084ec5a547",
},
},
.paths = .{
Expand Down
4 changes: 2 additions & 2 deletions src/Translator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {
bare_name = typedef_name;
name = typedef_name;
} else {
if (bare_name[0] == '(') {
if (enum_ty.isAnonymous(t.comp)) {
bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
is_unnamed = true;
}
Expand Down Expand Up @@ -947,7 +947,7 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex
},
.@"enum" => |enum_ty| {
var trans_scope = scope;
const is_anonymous = enum_ty.name.lookup(t.comp)[0] == '(';
const is_anonymous = enum_ty.isAnonymous(t.comp);
if (is_anonymous) {
if (t.weak_global_names.contains(enum_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
}
Expand Down
10 changes: 10 additions & 0 deletions src/helpers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ test "ArithmeticConversion" {
try Test.checkPromotion(c_uint, c_long, c_long);

try Test.checkPromotion(c_ulong, c_longlong, c_ulonglong);

// stdint.h
try Test.checkPromotion(u8, i8, c_int);
try Test.checkPromotion(u16, i16, c_int);
try Test.checkPromotion(i32, c_int, c_int);
try Test.checkPromotion(u32, c_int, c_uint);
try Test.checkPromotion(i64, c_int, c_long);
try Test.checkPromotion(u64, c_int, c_ulong);
try Test.checkPromotion(isize, c_int, c_long);
try Test.checkPromotion(usize, c_int, c_ulong);
}

const F_SUFFIX = @import("helpers/F_SUFFIX.zig").F_SUFFIX;
Expand Down
6 changes: 4 additions & 2 deletions src/helpers/CAST_OR_CALL.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const cast = @import("cast.zig").cast;
const __helpers = struct {
const cast = @import("cast.zig").cast;
};
// BEGIN_SOURCE
/// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
/// could be either: cast B to A, or call A with the value B.
Expand All @@ -8,7 +10,7 @@ pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
} {
switch (@typeInfo(@TypeOf(a))) {
.type => return cast(a, b),
.type => return __helpers.cast(a, b),
.@"fn" => return a(b),
else => unreachable, // return type will be a compile error otherwise
}
Expand Down
8 changes: 5 additions & 3 deletions src/helpers/LL_SUFFIX.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
const __helpers = struct {
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
};
// BEGIN_SOURCE
pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_longlong, n, .decimal)) {
return promoteIntLiteral(c_longlong, n, .decimal);
pub fn LL_SUFFIX(comptime n: comptime_int) @TypeOf(__helpers.promoteIntLiteral(c_longlong, n, .decimal)) {
return __helpers.promoteIntLiteral(c_longlong, n, .decimal);
}
10 changes: 6 additions & 4 deletions src/helpers/L_SUFFIX.zig
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
const __helpers = struct {
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
};
// BEGIN_SOURCE
fn L_SUFFIX_ReturnType(comptime number: anytype) type {
switch (@typeInfo(@TypeOf(number))) {
.int, .comptime_int => return @TypeOf(promoteIntLiteral(c_long, number, .decimal)),
.int, .comptime_int => return @TypeOf(__helpers.promoteIntLiteral(c_long, number, .decimal)),
.float, .comptime_float => return c_longdouble,
else => @compileError("Invalid value for L suffix"),
}
}
pub fn L_SUFFIX(comptime number: anytype) L_SUFFIX_ReturnType(number) {
pub fn L_SUFFIX(comptime number: anytype) @This().L_SUFFIX_ReturnType(number) {
switch (@typeInfo(@TypeOf(number))) {
.int, .comptime_int => return promoteIntLiteral(c_long, number, .decimal),
.int, .comptime_int => return __helpers.promoteIntLiteral(c_long, number, .decimal),
.float, .comptime_float => @compileError("TODO: c_longdouble initialization from comptime_float not supported"),
else => @compileError("Invalid value for L suffix"),
}
Expand Down
8 changes: 5 additions & 3 deletions src/helpers/ULL_SUFFIX.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
const __helpers = struct {
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
};
// BEGIN_SOURCE
pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulonglong, n, .decimal)) {
return promoteIntLiteral(c_ulonglong, n, .decimal);
pub fn ULL_SUFFIX(comptime n: comptime_int) @TypeOf(__helpers.promoteIntLiteral(c_ulonglong, n, .decimal)) {
return __helpers.promoteIntLiteral(c_ulonglong, n, .decimal);
}
8 changes: 5 additions & 3 deletions src/helpers/UL_SUFFIX.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
const __helpers = struct {
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
};
// BEGIN_SOURCE
pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_ulong, n, .decimal)) {
return promoteIntLiteral(c_ulong, n, .decimal);
pub fn UL_SUFFIX(comptime n: comptime_int) @TypeOf(__helpers.promoteIntLiteral(c_ulong, n, .decimal)) {
return __helpers.promoteIntLiteral(c_ulong, n, .decimal);
}
8 changes: 5 additions & 3 deletions src/helpers/U_SUFFIX.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
const __helpers = struct {
const promoteIntLiteral = @import("promote_int_literal.zig").promoteIntLiteral;
};
// BEGIN_SOURCE
pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(promoteIntLiteral(c_uint, n, .decimal)) {
return promoteIntLiteral(c_uint, n, .decimal);
pub fn U_SUFFIX(comptime n: comptime_int) @TypeOf(__helpers.promoteIntLiteral(c_uint, n, .decimal)) {
return __helpers.promoteIntLiteral(c_uint, n, .decimal);
}
37 changes: 23 additions & 14 deletions src/helpers/arithmetic_conversion.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ pub fn ArithmeticConversion(comptime A: type, comptime B: type) type {
if (A == f64 or B == f64) return f64;
if (A == f32 or B == f32) return f32;

const A_Promoted = PromotedIntType(A);
const B_Promoted = PromotedIntType(B);
const A_Promoted = @This().PromotedIntType(A);
const B_Promoted = @This().PromotedIntType(B);
const std = @import("std");
comptime {
std.debug.assert(integerRank(A_Promoted) >= integerRank(c_int));
std.debug.assert(integerRank(B_Promoted) >= integerRank(c_int));
std.debug.assert(@This().integerRank(A_Promoted) >= @This().integerRank(c_int));
std.debug.assert(@This().integerRank(B_Promoted) >= @This().integerRank(c_int));
}

if (A_Promoted == B_Promoted) return A_Promoted;
Expand All @@ -19,31 +19,40 @@ pub fn ArithmeticConversion(comptime A: type, comptime B: type) type {
const b_signed = @typeInfo(B_Promoted).int.signedness == .signed;

if (a_signed == b_signed) {
return if (integerRank(A_Promoted) > integerRank(B_Promoted)) A_Promoted else B_Promoted;
return if (@This().integerRank(A_Promoted) > @This().integerRank(B_Promoted)) A_Promoted else B_Promoted;
}

const SignedType = if (a_signed) A_Promoted else B_Promoted;
const UnsignedType = if (!a_signed) A_Promoted else B_Promoted;

if (integerRank(UnsignedType) >= integerRank(SignedType)) return UnsignedType;
if (@This().integerRank(UnsignedType) >= @This().integerRank(SignedType)) return UnsignedType;

if (std.math.maxInt(SignedType) >= std.math.maxInt(UnsignedType)) return SignedType;

return ToUnsigned(SignedType);
return @This().ToUnsigned(SignedType);
}

/// Integer promotion described in C11 6.3.1.1.2
fn PromotedIntType(comptime T: type) type {
return switch (T) {
bool, u8, i8, c_short => c_int,
bool, c_short => c_int,
c_ushort => if (@sizeOf(c_ushort) == @sizeOf(c_int)) c_uint else c_int,
c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong => T,
else => if (T == comptime_int) {
@compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required");
} else if (@typeInfo(T) == .int) {
@compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required");
} else {
@compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`");
else => switch (@typeInfo(T)) {
.comptime_int => @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required"),
// promote to c_int if it can represent all values of T
.int => |int_info| if (int_info.bits < @bitSizeOf(c_int))
c_int
// otherwise, restore the original C type
else if (int_info.bits == @bitSizeOf(c_int))
if (int_info.signedness == .unsigned) c_uint else c_int
else if (int_info.bits <= @bitSizeOf(c_long))
if (int_info.signedness == .unsigned) c_ulong else c_long
else if (int_info.bits <= @bitSizeOf(c_longlong))
if (int_info.signedness == .unsigned) c_ulonglong else c_longlong
else
@compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required"),
else => @compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`"),
},
};
}
Expand Down
14 changes: 8 additions & 6 deletions src/helpers/div.zig
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const ArithmeticConversion = @import("arithmetic_conversion.zig").ArithmeticConversion;
const cast = @import("cast.zig").cast;
const __helpers = struct {
const ArithmeticConversion = @import("arithmetic_conversion.zig").ArithmeticConversion;
const cast = @import("cast.zig").cast;
};
// BEGIN_SOURCE
pub fn div(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
const a_casted = cast(ResType, a);
const b_casted = cast(ResType, b);
pub fn div(a: anytype, b: anytype) __helpers.ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
const ResType = __helpers.ArithmeticConversion(@TypeOf(a), @TypeOf(b));
const a_casted = __helpers.cast(ResType, a);
const b_casted = __helpers.cast(ResType, b);
switch (@typeInfo(ResType)) {
.float => return a_casted / b_casted,
.int => return @divTrunc(a_casted, b_casted),
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/promote_int_literal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
pub fn promoteIntLiteral(
comptime SuffixType: type,
comptime number: comptime_int,
comptime base: CIntLiteralBase,
) PromoteIntLiteralReturnType(SuffixType, number, base) {
comptime base: @This().CIntLiteralBase,
) @This().PromoteIntLiteralReturnType(SuffixType, number, base) {
return number;
}

const CIntLiteralBase = enum { decimal, octal, hex };

fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime base: CIntLiteralBase) type {
fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime base: @This().CIntLiteralBase) type {
const signed_decimal = [_]type{ c_int, c_long, c_longlong, c_ulonglong };
const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong };
const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong };
Expand Down
18 changes: 10 additions & 8 deletions src/helpers/rem.zig
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
const ArithmeticConversion = @import("arithmetic_conversion.zig").ArithmeticConversion;
const cast = @import("cast.zig").cast;
const signedRemainder = @import("signed_remainder.zig").signedRemainder;
const __helpers = struct {
const ArithmeticConversion = @import("arithmetic_conversion.zig").ArithmeticConversion;
const cast = @import("cast.zig").cast;
const signedRemainder = @import("signed_remainder.zig").signedRemainder;
};
// BEGIN_SOURCE
pub fn rem(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
const a_casted = cast(ResType, a);
const b_casted = cast(ResType, b);
pub fn rem(a: anytype, b: anytype) __helpers.ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
const ResType = __helpers.ArithmeticConversion(@TypeOf(a), @TypeOf(b));
const a_casted = __helpers.cast(ResType, a);
const b_casted = __helpers.cast(ResType, b);
switch (@typeInfo(ResType)) {
.int => {
if (@typeInfo(ResType).int.signedness == .signed) {
return signedRemainder(a_casted, b_casted);
return __helpers.signedRemainder(a_casted, b_casted);
} else {
return a_casted % b_casted;
}
Expand Down
25 changes: 22 additions & 3 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@ pub fn main() u8 {
};
defer comp.deinit();

var driver: aro.Driver = .{ .comp = &comp };
const exe_name = std.fs.selfExePathAlloc(gpa) catch {
std.debug.print("unable to find translate-c executable path\n", .{});
if (fast_exit) process.exit(1);
return 1;
};
defer gpa.free(exe_name);

var driver: aro.Driver = .{ .comp = &comp, .aro_name = exe_name };
defer driver.deinit();

translate(&driver, args) catch |err| switch (err) {
var toolchain: aro.Toolchain = .{ .driver = &driver, .arena = arena, .filesystem = .{ .real = comp.cwd } };
defer toolchain.deinit();

translate(&driver, &toolchain, args) catch |err| switch (err) {
error.OutOfMemory => {
std.debug.print("ran out of memory translating\n", .{});
if (fast_exit) process.exit(1);
Expand All @@ -50,7 +60,7 @@ pub fn main() u8 {
return @intFromBool(comp.diagnostics.errors != 0);
}

fn translate(d: *aro.Driver, args: []const []const u8) !void {
fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const []const u8) !void {
const gpa = d.comp.gpa;

var macro_buf = std.ArrayList(u8).init(gpa);
Expand All @@ -64,6 +74,15 @@ fn translate(d: *aro.Driver, args: []const []const u8) !void {
}
const source = d.inputs.items[0];

tc.discover() catch |er| switch (er) {
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", .{}),
};

const builtin_macros = d.comp.generateBuiltinMacros(.include_system_defines, null) catch |err| switch (err) {
error.StreamTooLong => return d.fatal("builtin macro source exceeded max size", .{}),
else => |e| return e,
Expand Down
Loading
Loading