From 715d808f14e132df9eb4e29261d010eca5cfb82a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 11 May 2019 10:33:41 +0200 Subject: [PATCH 1/3] linux: Fix clock_gettime on systems w/o VDSO --- std/os/linux.zig | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/std/os/linux.zig b/std/os/linux.zig index 99a29db582..3b0d0cc109 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -959,10 +959,13 @@ pub fn waitpid(pid: i32, status: *i32, options: i32) usize { return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); } +var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); + pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { if (VDSO_CGT_SYM.len != 0) { - const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); - if (@ptrToInt(f) != 0) { + const ptr = @atomicLoad(?*const c_void, &vdso_clock_gettime, .Unordered); + if (ptr) |fn_ptr| { + const f = @ptrCast(@typeOf(clock_gettime), fn_ptr); const rc = f(clk_id, tp); switch (rc) { 0, @bitCast(usize, isize(-EINVAL)) => return rc, @@ -972,13 +975,18 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { } return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } -var vdso_clock_gettime = init_vdso_clock_gettime; + extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { - const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); - var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); - _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); - if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); - return f(clk, ts); + const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM)); + // Note that we may not have a VDSO at all, update the stub address anyway + // so that clock_gettime will fall back on the good old (and slow) syscall + _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic); + // Call into the VDSO if available + if (ptr) |fn_ptr| { + const f = @ptrCast(@typeOf(clock_gettime), fn_ptr); + return f(clk, ts); + } + return @bitCast(usize, isize(-ENOSYS)); } pub fn clock_getres(clk_id: i32, tp: *timespec) usize { From 1b23348f30243ed61a61f1892bb391d8afab7f10 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 11 May 2019 10:34:22 +0200 Subject: [PATCH 2/3] linux: Minor `zig fmt` induced reformatting --- std/os/linux.zig | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/std/os/linux.zig b/std/os/linux.zig index 3b0d0cc109..1e121b81c8 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -1112,8 +1112,8 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti const NSIG = 65; const sigset_t = [128 / @sizeOf(usize)]usize; -const all_mask = []u32{0xffffffff, 0xffffffff}; -const app_mask = []u32{0xfffffffc, 0x7fffffff}; +const all_mask = []u32{ 0xffffffff, 0xffffffff }; +const app_mask = []u32{ 0xfffffffc, 0x7fffffff }; const k_sigaction = extern struct { handler: extern fn (i32) void, @@ -1411,9 +1411,15 @@ pub const epoll_data = extern union { // On x86_64 the structure is packed so that it matches the definition of its // 32bit counterpart pub const epoll_event = if (builtin.arch != .x86_64) - extern struct { events: u32, data: epoll_data } - else - packed struct { events: u32, data: epoll_data }; + extern struct { + events: u32, + data: epoll_data, + } +else + packed struct { + events: u32, + data: epoll_data, + }; pub fn epoll_create() usize { return epoll_create1(0); From 10e9d47b499752f5c40e8ded1bf50385d84912ff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 11 May 2019 12:05:33 -0400 Subject: [PATCH 3/3] stage2 translate-c: implement functions with no prototype stage1 translate-c actually has this wrong. When exporting a function, it's ok to use empty parameters. But for prototypes, "no prototype" means that it has to be emitted as a function that accepts anything, e.g. extern fn foo(...) void; See #1964 --- src-self-hosted/translate_c.zig | 112 +++++++++++++++++++++----------- test/tests.zig | 10 ++- test/translate_c.zig | 46 +++++++++---- 3 files changed, 116 insertions(+), 52 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index e7d6bfd09c..703f907907 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -254,11 +254,20 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const fn_qt = ZigClangFunctionDecl_getType(fn_decl); const fn_type = ZigClangQualType_getTypePtr(fn_qt); var scope = &c.global_scope.base; + const has_body = ZigClangFunctionDecl_hasBody(fn_decl); + const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); const decl_ctx = FnDeclContext{ .fn_name = fn_name, - .has_body = ZigClangFunctionDecl_hasBody(fn_decl), - .storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl), + .has_body = has_body, + .storage_class = storage_class, .scope = &scope, + .is_export = switch (storage_class) { + .None => has_body, + .Extern, .Static => false, + .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern"), + .Auto => unreachable, // Not legal on functions + .Register => unreachable, // Not legal on functions + }, }; const proto_node = switch (ZigClangType_getTypeClass(fn_type)) { .FunctionProto => blk: { @@ -270,7 +279,15 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { error.OutOfMemory => return error.OutOfMemory, }; }, - .FunctionNoProto => return failDecl(c, fn_decl_loc, fn_name, "TODO support functions with no prototype"), + .FunctionNoProto => blk: { + const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type); + break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) { + error.UnsupportedType => { + return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function"); + }, + error.OutOfMemory => return error.OutOfMemory, + }; + }, else => unreachable, }; @@ -432,8 +449,22 @@ const FnDeclContext = struct { has_body: bool, storage_class: ZigClangStorageClass, scope: **Scope, + is_export: bool, }; +fn transCC( + rp: RestorePoint, + fn_ty: *const ZigClangFunctionType, + source_loc: ZigClangSourceLocation, +) !CallingConvention { + const clang_cc = ZigClangFunctionType_getCallConv(fn_ty); + switch (clang_cc) { + .C => return CallingConvention.C, + .X86StdCall => return CallingConvention.Stdcall, + else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: {}", @tagName(clang_cc)), + } +} + fn transFnProto( rp: RestorePoint, fn_proto_ty: *const ZigClangFunctionProtoType, @@ -441,52 +472,44 @@ fn transFnProto( fn_decl_context: ?FnDeclContext, ) !*ast.Node.FnProto { const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty); - const cc = switch (ZigClangFunctionType_getCallConv(fn_ty)) { - .C => CallingConvention.C, - .X86StdCall => CallingConvention.Stdcall, - .X86FastCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 fastcall"), - .X86ThisCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 thiscall"), - .X86VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 vectorcall"), - .X86Pascal => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 pascal"), - .Win64 => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: win64"), - .X86_64SysV => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 64sysv"), - .X86RegCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 reg"), - .AAPCS => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs"), - .AAPCS_VFP => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs-vfp"), - .IntelOclBicc => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: intel_ocl_bicc"), - .SpirFunction => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: SPIR function"), - .OpenCLKernel => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: OpenCLKernel"), - .Swift => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: Swift"), - .PreserveMost => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveMost"), - .PreserveAll => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveAll"), - .AArch64VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: AArch64VectorCall"), - }; - + const cc = try transCC(rp, fn_ty, source_loc); const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty); const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty); var i: usize = 0; while (i < param_count) : (i += 1) { return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType"); } + + return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc); +} + +fn transFnNoProto( + rp: RestorePoint, + fn_ty: *const ZigClangFunctionType, + source_loc: ZigClangSourceLocation, + fn_decl_context: ?FnDeclContext, +) !*ast.Node.FnProto { + const cc = try transCC(rp, fn_ty, source_loc); + const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true; + return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc); +} + +fn finishTransFnProto( + rp: RestorePoint, + fn_ty: *const ZigClangFunctionType, + source_loc: ZigClangSourceLocation, + fn_decl_context: ?FnDeclContext, + is_var_args: bool, + cc: CallingConvention, +) !*ast.Node.FnProto { + const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; + // TODO check for always_inline attribute // TODO check for align attribute // pub extern fn name(...) T const pub_tok = try appendToken(rp.c, .Keyword_pub, "pub"); const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null; - const is_export = exp: { - const decl_ctx = fn_decl_context orelse break :exp false; - break :exp switch (decl_ctx.storage_class) { - .None => switch (rp.c.mode) { - .import => false, - .translate => decl_ctx.has_body, - }, - .Extern, .Static => false, - .PrivateExtern => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported storage class: private extern"), - .Auto => unreachable, // Not legal on functions - .Register => unreachable, // Not legal on functions - }; - }; const extern_export_inline_tok = if (is_export) try appendToken(rp.c, .Keyword_export, "export") else if (cc == .C) @@ -527,7 +550,7 @@ fn transFnProto( .name_token = name_tok, .params = ast.Node.FnProto.ParamList.init(rp.c.a()), .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node }, - .var_args_token = var_args_tok, + .var_args_token = null, // TODO this field is broken in the AST data model .extern_export_inline_token = extern_export_inline_tok, .cc_token = cc_tok, .async_attr = null, @@ -536,6 +559,19 @@ fn transFnProto( .align_expr = null, .section_expr = null, }; + if (is_var_args) { + const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl); + var_arg_node.* = ast.Node.ParamDecl{ + .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, + .doc_comments = null, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, + .var_args_token = var_args_tok, + }; + try fn_proto.params.push(&var_arg_node.base); + } return fn_proto; } diff --git a/test/tests.zig b/test/tests.zig index fec6a294ff..4b67cf0f6c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1076,6 +1076,14 @@ pub const TranslateCContext = struct { } pub fn add_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + for ([]bool{ false, true }) |stage2| { + const tc = self.create(false, "source.h", name, source, expected_lines); + tc.stage2 = stage2; + self.addCase(tc); + } + } + + pub fn addC_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { for ([]bool{ false, true }) |stage2| { const tc = self.create(false, "source.c", name, source, expected_lines); tc.stage2 = stage2; @@ -1084,7 +1092,7 @@ pub const TranslateCContext = struct { } pub fn add_2(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { - const tc = self.create(false, "source.c", name, source, expected_lines); + const tc = self.create(false, "source.h", name, source, expected_lines); tc.stage2 = true; self.addCase(tc); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 6a2d2ef1d9..930442f293 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,13 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); +// add_both - test for stage1 and stage2, in #include mode +// add - test stage1 only, in #include mode +// add_2 - test stage2 only, in #include mode +// addC_both - test for stage1 and stage2, in -c mode +// addC - test stage1 only, in -c mode +// addC_2 - test stage2 only, in -c mode + pub fn addCases(cases: *tests.TranslateCContext) void { /////////////// Cases that pass for both stage1/stage2 //////////////// cases.add_both("simple function prototypes", @@ -11,25 +18,29 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn bar() c_int; ); - cases.add_both("simple function definition", - \\void foo(void) {}; - , - \\pub export fn foo() void {} - ); - /////////////// Cases that pass for only stage2 //////////////// - // (none) + cases.add_2("Parameterless function prototypes", + \\void a() {} + \\void b(void) {} + \\void c(); + \\void d(void); + , + \\pub export fn a() void {} + \\pub export fn b() void {} + \\pub extern fn c(...) void; + \\pub extern fn d() void; + ); - /////////////// Cases that pass for only stage1 //////////////// - - cases.addC("Parameterless function prototypes", - \\void foo() {} - \\void bar(void) {} + cases.add_2("simple function definition", + \\void foo(void) {} + \\static void bar(void) {} , \\pub export fn foo() void {} - \\pub export fn bar() void {} + \\pub extern fn bar() void {} ); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// + cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) , @@ -1681,4 +1692,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} ); + + /////////////// Cases for only stage1 because stage2 behavior is better //////////////// + cases.addC("Parameterless function prototypes", + \\void foo() {} + \\void bar(void) {} + , + \\pub export fn foo() void {} + \\pub export fn bar() void {} + ); }