translate-c: promote macros that reference var decls to inline functions

This commit is contained in:
freakmangd 2024-06-05 16:06:51 -04:00 committed by GitHub
parent 8f27fdb84e
commit 9bbfb09fc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 32 deletions

View File

@ -1381,7 +1381,6 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
pub const Root = struct { pub const Root = struct {
base: ScopeExtraScope, base: ScopeExtraScope,
sym_table: SymbolTable, sym_table: SymbolTable,
macro_table: SymbolTable,
blank_macros: std.StringArrayHashMap(void), blank_macros: std.StringArrayHashMap(void),
context: *ScopeExtraContext, context: *ScopeExtraContext,
nodes: std.ArrayList(ast.Node), nodes: std.ArrayList(ast.Node),
@ -1393,7 +1392,6 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
.parent = null, .parent = null,
}, },
.sym_table = SymbolTable.init(c.gpa), .sym_table = SymbolTable.init(c.gpa),
.macro_table = SymbolTable.init(c.gpa),
.blank_macros = std.StringArrayHashMap(void).init(c.gpa), .blank_macros = std.StringArrayHashMap(void).init(c.gpa),
.context = c, .context = c,
.nodes = std.ArrayList(ast.Node).init(c.gpa), .nodes = std.ArrayList(ast.Node).init(c.gpa),
@ -1402,7 +1400,6 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
pub fn deinit(scope: *Root) void { pub fn deinit(scope: *Root) void {
scope.sym_table.deinit(); scope.sym_table.deinit();
scope.macro_table.deinit();
scope.blank_macros.deinit(); scope.blank_macros.deinit();
scope.nodes.deinit(); scope.nodes.deinit();
} }
@ -1410,7 +1407,7 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
/// Check if the global scope contains this name, without looking into the "future", e.g. /// Check if the global scope contains this name, without looking into the "future", e.g.
/// ignore the preprocessed decl and macro names. /// ignore the preprocessed decl and macro names.
pub fn containsNow(scope: *Root, name: []const u8) bool { pub fn containsNow(scope: *Root, name: []const u8) bool {
return scope.sym_table.contains(name) or scope.macro_table.contains(name); return scope.sym_table.contains(name);
} }
/// Check if the global scope contains the name, includes all decls that haven't been translated yet. /// Check if the global scope contains the name, includes all decls that haven't been translated yet.

View File

@ -875,6 +875,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
.declaration => unreachable, .declaration => unreachable,
.warning => { .warning => {
const payload = node.castTag(.warning).?.data; const payload = node.castTag(.warning).?.data;
try c.buf.append('\n');
try c.buf.appendSlice(payload); try c.buf.appendSlice(payload);
try c.buf.append('\n'); try c.buf.append('\n');
return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'

View File

@ -177,7 +177,6 @@ pub fn translate(
try transPreprocessorEntities(&context, ast_unit); try transPreprocessorEntities(&context, ast_unit);
try addMacros(&context);
for (context.alias_list.items) |alias| { for (context.alias_list.items) |alias| {
const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name }); const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
try addTopLevelDecl(&context, alias.alias, node); try addTopLevelDecl(&context, alias.alias, node);
@ -5105,6 +5104,7 @@ const MacroCtx = struct {
i: usize = 0, i: usize = 0,
loc: clang.SourceLocation, loc: clang.SourceLocation,
name: []const u8, name: []const u8,
refs_var_decl: bool = false,
fn peek(self: *MacroCtx) ?CToken.Id { fn peek(self: *MacroCtx) ?CToken.Id {
if (self.i >= self.list.len) return null; if (self.i >= self.list.len) return null;
@ -5244,7 +5244,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
// We define it as an empty string so that it can still be used with ++ // We define it as an empty string so that it can still be used with ++
const str_node = try Tag.string_literal.create(c.arena, "\"\""); const str_node = try Tag.string_literal.create(c.arena, "\"\"");
const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = name, .init = str_node }); const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = name, .init = str_node });
try c.global_scope.macro_table.put(name, var_decl); try addTopLevelDecl(c, name, var_decl);
try c.global_scope.blank_macros.put(name, {}); try c.global_scope.blank_macros.put(name, {});
continue; continue;
}, },
@ -5291,7 +5291,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
try c.global_scope.blank_macros.put(m.name, {}); try c.global_scope.blank_macros.put(m.name, {});
const init_node = try Tag.string_literal.create(c.arena, "\"\""); const init_node = try Tag.string_literal.create(c.arena, "\"\"");
const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
try c.global_scope.macro_table.put(m.name, var_decl); try addTopLevelDecl(c, m.name, var_decl);
return; return;
}, },
else => {}, else => {},
@ -5304,8 +5304,32 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
if (last != .eof and last != .nl) if (last != .eof and last != .nl)
return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()}); return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); const node = node: {
try c.global_scope.macro_table.put(m.name, var_decl); const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
if (getFnProto(c, var_decl)) |proto_node| {
// If a macro aliases a global variable which is a function pointer, we conclude that
// the macro is intended to represent a function that assumes the function pointer
// variable is non-null and calls it.
break :node try transCreateNodeMacroFn(c, m.name, var_decl, proto_node);
} else if (m.refs_var_decl) {
const return_type = try Tag.typeof.create(c.arena, init_node);
const return_expr = try Tag.@"return".create(c.arena, init_node);
const block = try Tag.block_single.create(c.arena, return_expr);
try warn(c, scope, m.loc, "macro '{s}' contains a runtime value, translated to function", .{m.name});
break :node try Tag.pub_inline_fn.create(c.arena, .{
.name = m.name,
.params = &.{},
.return_type = return_type,
.body = block,
});
}
break :node var_decl;
};
try addTopLevelDecl(c, m.name, node);
} }
fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
@ -5315,7 +5339,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
.name = m.name, .name = m.name,
.init = try Tag.helpers_macro.create(c.arena, pattern.impl), .init = try Tag.helpers_macro.create(c.arena, pattern.impl),
}); });
try c.global_scope.macro_table.put(m.name, decl); try addTopLevelDecl(c, m.name, decl);
return; return;
} }
@ -5380,7 +5404,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
.return_type = return_type, .return_type = return_type,
.body = try block_scope.complete(c), .body = try block_scope.complete(c),
}); });
try c.global_scope.macro_table.put(m.name, fn_decl); try addTopLevelDecl(c, m.name, fn_decl);
} }
const ParseError = Error || error{ParseError}; const ParseError = Error || error{ParseError};
@ -5768,6 +5792,11 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty); if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);
const identifier = try Tag.identifier.create(c.arena, mangled_name); const identifier = try Tag.identifier.create(c.arena, mangled_name);
scope.skipVariableDiscard(identifier.castTag(.identifier).?.data); scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
refs_var: {
const ident_node = c.global_scope.sym_table.get(slice) orelse break :refs_var;
const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
if (!var_decl_node.data.is_const) m.refs_var_decl = true;
}
return identifier; return identifier;
}, },
.l_paren => { .l_paren => {
@ -6496,17 +6525,3 @@ fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
} }
return null; return null;
} }
fn addMacros(c: *Context) !void {
var it = c.global_scope.macro_table.iterator();
while (it.next()) |entry| {
if (getFnProto(c, entry.value_ptr.*)) |proto_node| {
// If a macro aliases a global variable which is a function pointer, we conclude that
// the macro is intended to represent a function that assumes the function pointer
// variable is non-null and calls it.
try addTopLevelDecl(c, entry.key_ptr.*, try transCreateNodeMacroFn(c, entry.key_ptr.*, entry.value_ptr.*, proto_node));
} else {
try addTopLevelDecl(c, entry.key_ptr.*, entry.value_ptr.*);
}
}
}

View File

@ -0,0 +1,21 @@
extern float foo;
#define FOO_TWICE foo * 2.0f
#define FOO_NEGATIVE -foo
#define BAR 10.0f
#define BAR_TWICE BAR * 2.0f
// translate-c
// c_frontend=clang
//
// pub extern var foo: f32;
//
// pub inline fn FOO_TWICE() @TypeOf(foo * @as(f32, 2.0)) {
// return foo * @as(f32, 2.0);
// }
//
// pub inline fn FOO_NEGATIVE() @TypeOf(-foo) {
// return -foo;
// }
// pub const BAR = @as(f32, 10.0);
// pub const BAR_TWICE = BAR * @as(f32, 2.0);

View File

@ -223,7 +223,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ | (*((unsigned char *)(p) + 1) << 8) \ \\ | (*((unsigned char *)(p) + 1) << 8) \
\\ | (*((unsigned char *)(p) + 2) << 16)) \\ | (*((unsigned char *)(p) + 2) << 16))
, &[_][]const u8{ , &[_][]const u8{
\\pub const FOO = (foo + @as(c_int, 2)).*; \\pub inline fn FOO() @TypeOf((foo + @as(c_int, 2)).*) {
\\ return (foo + @as(c_int, 2)).*;
\\}
, ,
\\pub const VALUE = ((((@as(c_int, 1) + (@as(c_int, 2) * @as(c_int, 3))) + (@as(c_int, 4) * @as(c_int, 5))) + @as(c_int, 6)) << @as(c_int, 7)) | @intFromBool(@as(c_int, 8) == @as(c_int, 9)); \\pub const VALUE = ((((@as(c_int, 1) + (@as(c_int, 2) * @as(c_int, 3))) + (@as(c_int, 4) * @as(c_int, 5))) + @as(c_int, 6)) << @as(c_int, 7)) | @intFromBool(@as(c_int, 8) == @as(c_int, 9));
, ,
@ -452,7 +454,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define FOO -\ \\#define FOO -\
\\BAR \\BAR
, &[_][]const u8{ , &[_][]const u8{
\\pub const FOO = -BAR; \\pub inline fn FOO() @TypeOf(-BAR) {
\\ return -BAR;
\\}
}); });
cases.add("struct with atomic field", cases.add("struct with atomic field",
@ -2453,9 +2457,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ _ = c.*.b; \\ _ = c.*.b;
\\} \\}
, ,
\\pub const DOT = a.b; \\pub inline fn ARROW() @TypeOf(a.*.b) {
\\ return a.*.b;
\\}
, ,
\\pub const ARROW = a.*.b; \\pub inline fn DOT() @TypeOf(a.b) {
\\ return a.b;
\\}
}); });
cases.add("array access", cases.add("array access",
@ -2472,7 +2480,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return array[@as(c_uint, @intCast(index))]; \\ return array[@as(c_uint, @intCast(index))];
\\} \\}
, ,
\\pub const ACCESS = array[@as(usize, @intCast(@as(c_int, 2)))]; \\pub inline fn ACCESS() @TypeOf(array[@as(usize, @intCast(@as(c_int, 2)))]) {
\\ return array[@as(usize, @intCast(@as(c_int, 2)))];
\\}
}); });
cases.add("cast signed array index to unsigned", cases.add("cast signed array index to unsigned",
@ -3130,7 +3140,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ int a, b, c; \\ int a, b, c;
\\#define FOO a ? b : c \\#define FOO a ? b : c
, &[_][]const u8{ , &[_][]const u8{
\\pub const FOO = if (a) b else c; \\pub inline fn FOO() @TypeOf(if (a) b else c) {
\\ return if (a) b else c;
\\}
}); });
cases.add("do while as expr", cases.add("do while as expr",
@ -3624,7 +3636,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define FOO _ \\#define FOO _
\\int _ = 42; \\int _ = 42;
, &[_][]const u8{ , &[_][]const u8{
\\pub const FOO = @"_"; \\pub inline fn FOO() @TypeOf(@"_") {
\\ return @"_";
\\}
, ,
\\pub export var @"_": c_int = 42; \\pub export var @"_": c_int = 42;
}); });