Skip to content

Add support for C bitfields #4165

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

Closed
wants to merge 1 commit into from
Closed
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
7 changes: 7 additions & 0 deletions src-self-hosted/clang.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub const struct_ZigClangAPInt = @OpaqueType();
pub const struct_ZigClangAPSInt = @OpaqueType();
pub const struct_ZigClangAPFloat = @OpaqueType();
pub const struct_ZigClangASTContext = @OpaqueType();
pub const struct_ZigClangASTRecordLayout = @OpaqueType();
pub const struct_ZigClangASTUnit = @OpaqueType();
pub const struct_ZigClangArraySubscriptExpr = @OpaqueType();
pub const struct_ZigClangArrayType = @OpaqueType();
Expand Down Expand Up @@ -757,6 +758,9 @@ pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_Z
pub extern fn ZigClangSourceManager_getSpellingColumnNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
pub extern fn ZigClangSourceManager_getCharacterData(self: ?*const struct_ZigClangSourceManager, SL: struct_ZigClangSourceLocation) [*:0]const u8;
pub extern fn ZigClangASTContext_getPointerType(self: ?*const struct_ZigClangASTContext, T: struct_ZigClangQualType) struct_ZigClangQualType;
pub extern fn ZigClangASTContext_getASTRecordLayout(self: ?*const struct_ZigClangASTContext, D: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangASTRecordLayout;
pub extern fn ZigClangASTRecordLayout_getFieldCount(self: ?*const struct_ZigClangASTRecordLayout) c_uint;
pub extern fn ZigClangASTRecordLayout_getFieldOffset(self: ?*const struct_ZigClangASTRecordLayout, field_no: c_uint) u64;
pub extern fn ZigClangASTUnit_getASTContext(self: ?*struct_ZigClangASTUnit) ?*struct_ZigClangASTContext;
pub extern fn ZigClangASTUnit_getSourceManager(self: *struct_ZigClangASTUnit) *struct_ZigClangSourceManager;
pub extern fn ZigClangASTUnit_visitLocalTopLevelDecls(self: *struct_ZigClangASTUnit, context: ?*c_void, Fn: ?extern fn (?*c_void, *const struct_ZigClangDecl) bool) bool;
Expand Down Expand Up @@ -1072,6 +1076,9 @@ pub extern fn ZigClangParenExpr_getSubExpr(*const ZigClangParenExpr) *const ZigC

pub extern fn ZigClangFieldDecl_isAnonymousStructOrUnion(*const struct_ZigClangFieldDecl) bool;
pub extern fn ZigClangFieldDecl_isBitField(*const struct_ZigClangFieldDecl) bool;
pub extern fn ZigClangFieldDecl_isUnnamedBitfield(*const struct_ZigClangFieldDecl) bool;
pub extern fn ZigClangFieldDecl_getFieldIndex(*const struct_ZigClangFieldDecl) c_uint;
pub extern fn ZigClangFieldDecl_getBitWidthValue(*const struct_ZigClangFieldDecl, *const struct_ZigClangASTContext) c_uint;
pub extern fn ZigClangFieldDecl_getType(*const struct_ZigClangFieldDecl) struct_ZigClangQualType;
pub extern fn ZigClangFieldDecl_getLocation(*const struct_ZigClangFieldDecl) struct_ZigClangSourceLocation;

Expand Down
44 changes: 21 additions & 23 deletions src-self-hosted/translate_c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,8 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
break :blk opaque;
};

const layout_tok = try if (ZigClangRecordDecl_getPackedAttribute(record_decl))
const is_packed = ZigClangRecordDecl_getPackedAttribute(record_decl);
const layout_tok = try if (is_packed)
appendToken(c, .Keyword_packed, "packed")
else
appendToken(c, .Keyword_extern, "extern");
Expand All @@ -768,33 +769,39 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
.rbrace_token = undefined,
};

const layout = ZigClangASTContext_getASTRecordLayout(c.clang_context, record_def);
var it = ZigClangRecordDecl_field_begin(record_def);
const end_it = ZigClangRecordDecl_field_end(record_def);
while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
const field_loc = ZigClangFieldDecl_getLocation(field_decl);

if (ZigClangFieldDecl_isBitField(field_decl)) {
const opaque = try transCreateNodeOpaqueType(c);
semicolon = try appendToken(c, .Semicolon, ";");
try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
break :blk opaque;
}
const is_bitfield = ZigClangFieldDecl_isBitField(field_decl);
const offset = ZigClangASTRecordLayout_getFieldOffset(layout, ZigClangFieldDecl_getFieldIndex(field_decl));

var is_anon = false;
var raw_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl) or (is_bitfield and ZigClangFieldDecl_isUnnamedBitfield(field_decl))) {
raw_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()});
is_anon = true;
}
const field_name = try appendIdentifier(c, raw_name);
_ = try appendToken(c, .Colon, ":");
const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) {
error.UnsupportedType => {
try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name});
return null;
},
else => |e| return e,

const field_type = if (is_bitfield) ty: {
const field_qt = ZigClangFieldDecl_getType(field_decl);
const sign = if (cIsSignedInteger(field_qt)) "i" else "u";
const bit_width = ZigClangFieldDecl_getBitWidthValue(field_decl, c.clang_context);
const type_name = try std.fmt.allocPrint(c.a(), "{}{}", .{ sign, bit_width });
break :ty try transCreateNodeIdentifier(c, type_name);
} else ty: {
break :ty transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) {
error.UnsupportedType => {
try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name});
return null;
},
else => |e| return e,
};
};

const field_node = try c.a().create(ast.Node.ContainerField);
Expand Down Expand Up @@ -3459,15 +3466,6 @@ fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocatio
const record_decl = ZigClangRecordType_getDecl(record_ty);
const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse
return true;
var it = ZigClangRecordDecl_field_begin(record_def);
const end_it = ZigClangRecordDecl_field_end(record_def);
while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
const field_decl = ZigClangRecordDecl_field_iterator_deref(it);

if (ZigClangFieldDecl_isBitField(field_decl)) {
return true;
}
}
return false;
},
.Elaborated => {
Expand Down
16 changes: 5 additions & 11 deletions src/analyze.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1682,18 +1682,12 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
*result = true;
return ErrorNone;
case ZigTypeIdInt:
switch (type_entry->data.integral.bit_count) {
case 8:
case 16:
case 32:
case 64:
case 128:
*result = true;
return ErrorNone;
default:
*result = false;
return ErrorNone;
if (type_entry->data.integral.bit_count <= 128) {
*result = true;
} else {
*result = false;
}
return ErrorNone;
case ZigTypeIdVector:
return type_allowed_in_extern(g, type_entry->data.vector.elem_type, result);
case ZigTypeIdFloat:
Expand Down
31 changes: 31 additions & 0 deletions src/zig_clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <clang/Frontend/CompilerInstance.h>
#include <clang/AST/APValue.h>
#include <clang/AST/Expr.h>
#include <clang/AST/RecordLayout.h>

#if __GNUC__ >= 8
#pragma GCC diagnostic pop
Expand Down Expand Up @@ -1504,6 +1505,20 @@ ZigClangQualType ZigClangASTContext_getPointerType(const ZigClangASTContext* sel
return bitcast(reinterpret_cast<const clang::ASTContext *>(self)->getPointerType(bitcast(T)));
}

const ZigClangASTRecordLayout *ZigClangASTContext_getASTRecordLayout(const ZigClangASTContext* self, const ZigClangRecordDecl *D) {
const clang::RecordDecl *record_decl = reinterpret_cast<const clang::RecordDecl *>(D);
const clang::ASTRecordLayout *result = &reinterpret_cast<const clang::ASTContext *>(self)->getASTRecordLayout(record_decl);
return reinterpret_cast<const ZigClangASTRecordLayout *>(result);
}

unsigned ZigClangASTRecordLayout_getFieldCount(const struct ZigClangASTRecordLayout *self) {
return reinterpret_cast<const clang::ASTRecordLayout *>(self)->getFieldCount();
}

uint64_t ZigClangASTRecordLayout_getFieldOffset(const ZigClangASTRecordLayout *self, unsigned field_no) {
return reinterpret_cast<const clang::ASTRecordLayout *>(self)->getFieldOffset(field_no);
}

unsigned ZigClangASTContext_getTypeAlign(const ZigClangASTContext* self, ZigClangQualType T) {
return reinterpret_cast<const clang::ASTContext *>(self)->getTypeAlign(bitcast(T));
}
Expand Down Expand Up @@ -2748,6 +2763,22 @@ bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *field_d
return reinterpret_cast<const clang::FieldDecl*>(field_decl)->isAnonymousStructOrUnion();
}

bool ZigClangFieldDecl_isUnnamedBitfield(const struct ZigClangFieldDecl *self) {
auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
return casted->isUnnamedBitfield();
}

unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *self) {
auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
return casted->getFieldIndex();
}

unsigned ZigClangFieldDecl_getBitWidthValue(const struct ZigClangFieldDecl *self, const struct ZigClangASTContext *ctx) {
auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
auto casted_ctx = reinterpret_cast<const clang::ASTContext *>(ctx);
return casted->getBitWidthValue(*casted_ctx);
}

ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *self) {
auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
return bitcast(casted->getLocation());
Expand Down
7 changes: 7 additions & 0 deletions src/zig_clang.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct ZigClangAPInt;
struct ZigClangAPSInt;
struct ZigClangAPValue;
struct ZigClangASTContext;
struct ZigClangASTRecordLayout;
struct ZigClangASTUnit;
struct ZigClangArraySubscriptExpr;
struct ZigClangArrayType;
Expand Down Expand Up @@ -831,7 +832,10 @@ ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const struct Zig
struct ZigClangSourceLocation SL);

ZIG_EXTERN_C struct ZigClangQualType ZigClangASTContext_getPointerType(const struct ZigClangASTContext*, struct ZigClangQualType T);
ZIG_EXTERN_C const ZigClangASTRecordLayout *ZigClangASTContext_getASTRecordLayout(const struct ZigClangASTContext* self, const ZigClangRecordDecl *D);

ZIG_EXTERN_C unsigned ZigClangASTRecordLayout_getFieldCount(const ZigClangASTRecordLayout *self);
ZIG_EXTERN_C uint64_t ZigClangASTRecordLayout_getFieldOffset(const ZigClangASTRecordLayout *self, unsigned field_no);

// Can return null.
ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char **args_end,
Expand Down Expand Up @@ -1130,8 +1134,11 @@ ZIG_EXTERN_C const char *ZigClangMacroDefinitionRecord_getName_getNameStart(cons
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getBegin(const struct ZigClangMacroDefinitionRecord *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getEnd(const struct ZigClangMacroDefinitionRecord *);

ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *self);
ZIG_EXTERN_C unsigned ZigClangFieldDecl_getBitWidthValue(const struct ZigClangFieldDecl *self, const struct ZigClangASTContext *ctx);
ZIG_EXTERN_C bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *);
ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *);
ZIG_EXTERN_C bool ZigClangFieldDecl_isUnnamedBitfield(const ZigClangFieldDecl *);
ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *);
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *);

Expand Down
15 changes: 15 additions & 0 deletions test/stage1/c_abi/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,18 @@ export fn zig_big_struct_both(x: BigStruct) BigStruct {
};
return s;
}

test "C struct with bit fields" {
const C_struct = extern struct {
a: u4,
b: u6,
c: i2,
d: u0, // 0-width field aligns to next byte boundary
e: u1,
};
std.testing.expectEqual(0, @bitOffsetOf(C_struct, "a"));
std.testing.expectEqual(4, @bitOffsetOf(C_struct, "b"));
std.testing.expectEqual(10, @bitOffsetOf(C_struct, "c"));
std.testing.expectEqual(16, @bitOffsetOf(C_struct, "d"));
std.testing.expectEqual(16, @bitOffsetOf(C_struct, "e"));
}
37 changes: 27 additions & 10 deletions test/translate_c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -347,18 +347,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
});

cases.add("pointer to struct demoted to opaque due to bit fields",
\\struct Foo {
\\ unsigned int: 1;
\\};
\\struct Bar {
\\ struct Foo *foo;
cases.add("bit fields",
\\struct bitfield {
\\ unsigned int a : 3;
\\ signed int b : 6;
\\ unsigned int c : 6;
\\ unsigned int d : 6;
\\ unsigned int : 0;
\\ unsigned long long e : 22;
\\ unsigned int a2 : 3;
\\ unsigned int b2 : 6;
\\ unsigned int c2 : 6;
\\ unsigned int d2 : 6;
\\ unsigned int : 0;
\\ unsigned long long e2 : 22;
\\};
, &[_][]const u8{
\\pub const struct_Foo = @OpaqueType();
,
\\pub const struct_Bar = extern struct {
\\ foo: ?*struct_Foo,
\\pub const struct_bitfield = extern struct {
\\ a: u3,
\\ b: i6,
\\ c: u6,
\\ d: u6,
\\ unnamed_1: u0,
\\ e: u22,
\\ a2: u3,
\\ b2: u6,
\\ c2: u6,
\\ d2: u6,
\\ unnamed_2: u0,
\\ e2: u22,
\\};
});

Expand Down