diff --git a/src/Translator.zig b/src/Translator.zig index 17d2fe6..b8452a6 100644 --- a/src/Translator.zig +++ b/src/Translator.zig @@ -13,6 +13,7 @@ const Type = aro.Type; const ast = @import("ast.zig"); const ZigNode = ast.Node; const ZigTag = ZigNode.Tag; +const builtins = @import("builtins.zig"); const Scope = @import("Scope.zig"); const Translator = @This(); @@ -55,6 +56,9 @@ tree: Tree, comp: *aro.Compilation, mapper: aro.TypeMapper, +rendered_builtins: std.EnumSet(builtins.Builtin) = .{}, +render_buf: std.ArrayList(u8), + fn getMangle(t: *Translator) u32 { t.mangle_count += 1; return t.mangle_count; @@ -137,7 +141,7 @@ pub fn translate( gpa: mem.Allocator, comp: *aro.Compilation, tree: aro.Tree, -) !std.zig.Ast { +) ![]u8 { const mapper = tree.comp.string_interner.getFastTypeMapper(tree.comp.gpa) catch tree.comp.string_interner.getSlowTypeMapper(); defer mapper.deinit(tree.comp.gpa); @@ -145,7 +149,7 @@ pub fn translate( defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var context: Translator = .{ + var translator: Translator = .{ .gpa = gpa, .arena = arena, .alias_list = .empty, @@ -154,39 +158,39 @@ pub fn translate( .comp = comp, .mapper = mapper, .tree = tree, + .render_buf = .init(gpa), }; - context.global_scope.* = Scope.Root.init(&context); + translator.global_scope.* = Scope.Root.init(&translator); defer { - context.decl_table.deinit(gpa); - context.alias_list.deinit(gpa); - context.global_names.deinit(gpa); - context.weak_global_names.deinit(gpa); - context.opaque_demotes.deinit(gpa); - context.unnamed_typedefs.deinit(gpa); - context.typedefs.deinit(gpa); - context.global_scope.deinit(); - context.pattern_list.deinit(gpa); - } - - inline for (@typeInfo(std.zig.c_builtins).@"struct".decls) |decl| { - const builtin_fn = try ZigTag.pub_var_simple.create(arena, .{ - .name = decl.name, - .init = try ZigTag.import_c_builtin.create(arena, decl.name), - }); - try addTopLevelDecl(&context, decl.name, builtin_fn); + translator.decl_table.deinit(gpa); + translator.alias_list.deinit(gpa); + translator.global_names.deinit(gpa); + translator.weak_global_names.deinit(gpa); + translator.opaque_demotes.deinit(gpa); + translator.unnamed_typedefs.deinit(gpa); + translator.typedefs.deinit(gpa); + translator.global_scope.deinit(); + translator.pattern_list.deinit(gpa); + translator.render_buf.deinit(); } - try prepopulateGlobalNameTable(&context); - try transTopLevelDecls(&context); + try prepopulateGlobalNameTable(&translator); + try transTopLevelDecls(&translator); - for (context.alias_list.items) |alias| { - if (!context.global_scope.sym_table.contains(alias.alias)) { + for (translator.alias_list.items) |alias| { + if (!translator.global_scope.sym_table.contains(alias.alias)) { const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name }); - try addTopLevelDecl(&context, alias.alias, node); + try addTopLevelDecl(&translator, alias.alias, node); } } - return ast.render(gpa, context.global_scope.nodes.items); + var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items); + defer { + gpa.free(zig_ast.source); + zig_ast.deinit(gpa); + } + try zig_ast.renderToArrayList(&translator.render_buf, .{}); + return translator.render_buf.toOwnedSlice(); } fn prepopulateGlobalNameTable(t: *Translator) !void { @@ -1352,6 +1356,9 @@ fn transExpr(t: *Translator, scope: *Scope, expr: NodeIndex, used: ResultUsed) T .shl_expr => return t.transShiftExpr(scope, expr, .shl, used), .shr_expr => return t.transShiftExpr(scope, expr, .shr, used), + + .builtin_call_expr => return t.transBuiltinCall(scope, expr, used), + .builtin_call_expr_one => return t.transBuiltinCall(scope, expr, used), else => unreachable, // Not an expression. } } @@ -1582,6 +1589,58 @@ fn transPointerArithmeticSignedOp( return t.transCreateNodeInfixOp(if (is_add) .add else .sub, lhs_node, bitcast_node, used); } +fn transBuiltinCall( + t: *Translator, + scope: *Scope, + call_node: NodeIndex, + used: ResultUsed, +) TransError!ZigNode { + // TODO would be nice to have a helper to extract the builtin name + const builtin_name, const arg_nodes = switch (t.nodeTag(call_node)) { + .builtin_call_expr => blk: { + const range = t.nodeData(call_node).range; + const name = t.tree.tokSlice(@intFromEnum(t.tree.data[range.start])); + const arg_nodes = t.tree.data[range.start + 1 .. range.end]; + break :blk .{ name, arg_nodes }; + }, + .builtin_call_expr_one => blk: { + const decl = t.nodeData(call_node).decl; + const name = t.tree.tokSlice(decl.name); + const ptr: [*]const NodeIndex = @ptrCast(&decl.node); + const slice = ptr[0..1]; + const end = std.mem.indexOfScalar(NodeIndex, slice, .none) orelse 1; + const arg_nodes = slice[0..end]; + break :blk .{ name, arg_nodes }; + }, + else => unreachable, + }; + const builtin = std.meta.stringToEnum(builtins.Builtin, builtin_name) orelse { + const call_loc = 0; // TODO builtin call source location + return t.fail(error.UnsupportedTranslation, call_loc, "TODO implement function '{s}' in std.zig.c_builtins", .{builtin_name}); + }; + + if (builtin.source()) |source| { + if (!t.rendered_builtins.contains(builtin)) { + t.rendered_builtins.insert(builtin); + try t.render_buf.appendSlice(source); + } + + const args = try t.arena.alloc(ZigNode, arg_nodes.len); + for (arg_nodes, args) |arg_node, *arg| { + arg.* = try t.transExpr(scope, arg_node, .used); + } + + const res = try ZigTag.call.create(t.arena, .{ + .lhs = try ZigTag.fn_identifier.create(t.arena, builtin_name), + .args = args, + }); + if (t.nodeType(call_node).is(.void)) return res; + return t.maybeSuppressResult(used, res); + } + + @panic("TODO transBulitinCall others"); +} + // ===================== // Node creation helpers // ===================== diff --git a/src/ast.zig b/src/ast.zig index 474cf06..b41c031 100644 --- a/src/ast.zig +++ b/src/ast.zig @@ -785,7 +785,7 @@ pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast { defer ctx.tokens.deinit(gpa); // Estimate that each top level node has 10 child nodes. - const estimated_node_count = nodes.len * 10; + const estimated_node_count = nodes.len * 10 + 1; // +1 for the .root node try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count); // Estimate that each each node has 2 tokens. const estimated_tokens_count = estimated_node_count * 2; diff --git a/src/builtins.zig b/src/builtins.zig new file mode 100644 index 0000000..15baddc --- /dev/null +++ b/src/builtins.zig @@ -0,0 +1,93 @@ +pub const Builtin = enum { + __builtin_bswap16, + __builtin_bswap32, + __builtin_bswap64, + __builtin_signbit, + __builtin_signbitf, + __builtin_popcount, + __builtin_ctz, + __builtin_clz, + __builtin_sqrt, + __builtin_sqrtf, + __builtin_sin, + __builtin_sinf, + __builtin_cos, + __builtin_cosf, + __builtin_exp, + __builtin_expf, + __builtin_exp2, + __builtin_exp2f, + __builtin_log, + __builtin_logf, + __builtin_log2, + __builtin_log2f, + __builtin_log10, + __builtin_log10f, + __builtin_abs, + __builtin_labs, + __builtin_llabs, + __builtin_fabs, + __builtin_fabsf, + __builtin_floor, + __builtin_floorf, + __builtin_ceil, + __builtin_ceilf, + __builtin_trunc, + __builtin_truncf, + __builtin_round, + __builtin_roundf, + __builtin_strlen, + __builtin_strcmp, + __builtin_object_size, + __builtin___memset_chk, + __builtin_memset, + __builtin___memcpy_chk, + __builtin_memcpy, + __builtin_nanf, + __builtin_huge_valf, + __builtin_inff, + __builtin_isnan, + __builtin_isinf, + __builtin_isinf_sign, + __has_builtin, + __builtin_assume, + __builtin_unreachable, + __builtin_constant_p, + __builtin_mul_overflow, + + // __builtin_alloca_with_align is not currently implemented. + // It is used in a run and a translate test to ensure that non-implemented + // builtins are correctly demoted. If you implement __builtin_alloca_with_align, + // please update the tests to use a different non-implemented builtin. + + pub fn source(b: Builtin) ?[]const u8 { + return switch (b) { + .__builtin_signbit => @embedFile("builtins/signbit.zig"), + .__builtin_signbitf => @embedFile("builtins/signbitf.zig"), + .__builtin_popcount => @embedFile("builtins/popcount.zig"), + .__builtin_ctz => @embedFile("builtins/ctz.zig"), + .__builtin_clz => @embedFile("builtins/clz.zig"), + .__builtin_abs => @embedFile("builtins/abs.zig"), + .__builtin_labs => @embedFile("builtins/labs.zig"), + .__builtin_llabs => @embedFile("builtins/llabs.zig"), + .__builtin_strlen => @embedFile("builtins/strlen.zig"), + .__builtin_strcmp => @embedFile("builtins/strcmp.zig"), + .__builtin_object_size => @embedFile("builtins/object_size.zig"), + .__builtin___memset_chk => @embedFile("builtins/memset_chk.zig"), + .__builtin_memset => @embedFile("builtins/memset.zig"), + .__builtin___memcpy_chk => @embedFile("builtins/memcpy_chk.zig"), + .__builtin_memcpy => @embedFile("builtins/memcpy.zig"), + .__builtin_nanf => @embedFile("builtins/nanf.zig"), + .__builtin_huge_valf => @embedFile("builtins/huge_valf.zig"), + .__builtin_inff => @embedFile("builtins/inff.zig"), + .__builtin_isnan => @embedFile("builtins/isnan.zig"), + .__builtin_isinf => @embedFile("builtins/isinf.zig"), + .__builtin_isinf_sign => @embedFile("builtins/isinf_sign.zig"), + .__has_builtin => @embedFile("builtins/has_builtin.zig"), + .__builtin_assume => @embedFile("builtins/assume.zig"), + .__builtin_constant_p => @embedFile("builtins/constant_p.zig"), + .__builtin_mul_overflow => @embedFile("builtins/mul_overflow.zig"), + else => return null, + }; + } +}; diff --git a/src/builtins/abs.zig b/src/builtins/abs.zig new file mode 100644 index 0000000..bb60893 --- /dev/null +++ b/src/builtins/abs.zig @@ -0,0 +1,4 @@ +/// Standard C Library bug: The absolute value of the most negative integer remains negative. +pub inline fn __builtin_abs(val: c_int) c_int { + return if (val == @import("std").math.minInt(c_int)) val else @intCast(@abs(val)); +} diff --git a/src/builtins/assume.zig b/src/builtins/assume.zig new file mode 100644 index 0000000..d66190e --- /dev/null +++ b/src/builtins/assume.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_assume(cond: bool) void { + if (!cond) unreachable; +} diff --git a/src/builtins/clz.zig b/src/builtins/clz.zig new file mode 100644 index 0000000..7ea3baf --- /dev/null +++ b/src/builtins/clz.zig @@ -0,0 +1,6 @@ +/// Returns the number of leading 0-bits in x, starting at the most significant bit position. +/// In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint +pub inline fn __builtin_clz(val: c_uint) c_int { + @setRuntimeSafety(false); + return @as(c_int, @bitCast(@as(c_uint, @clz(val)))); +} diff --git a/src/builtins/constant_p.zig b/src/builtins/constant_p.zig new file mode 100644 index 0000000..e737627 --- /dev/null +++ b/src/builtins/constant_p.zig @@ -0,0 +1,4 @@ +pub inline fn __builtin_constant_p(expr: anytype) c_int { + _ = expr; + return @intFromBool(false); +} diff --git a/src/builtins/ctz.zig b/src/builtins/ctz.zig new file mode 100644 index 0000000..9abb300 --- /dev/null +++ b/src/builtins/ctz.zig @@ -0,0 +1,6 @@ +/// Returns the number of trailing 0-bits in val, starting at the least significant bit position. +/// In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint +pub inline fn __builtin_ctz(val: c_uint) c_int { + @setRuntimeSafety(false); + return @as(c_int, @bitCast(@as(c_uint, @ctz(val)))); +} diff --git a/src/builtins/has_builtin.zig b/src/builtins/has_builtin.zig new file mode 100644 index 0000000..4de2baa --- /dev/null +++ b/src/builtins/has_builtin.zig @@ -0,0 +1,4 @@ +pub inline fn __has_builtin(func: anytype) c_int { + _ = func; + return @intFromBool(true); +} diff --git a/src/builtins/huge_valf.zig b/src/builtins/huge_valf.zig new file mode 100644 index 0000000..ac40738 --- /dev/null +++ b/src/builtins/huge_valf.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_huge_valf() f32 { + return @import("std").math.inf(f32); +} diff --git a/src/builtins/inff.zig b/src/builtins/inff.zig new file mode 100644 index 0000000..93d1b6a --- /dev/null +++ b/src/builtins/inff.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_inff() f32 { + return @import("std").math.inf(f32); +} diff --git a/src/builtins/isinf.zig b/src/builtins/isinf.zig new file mode 100644 index 0000000..ec3f624 --- /dev/null +++ b/src/builtins/isinf.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_isinf(x: anytype) c_int { + return @intFromBool(@import("std").math.isInf(x)); +} diff --git a/src/builtins/isinf_sign.zig b/src/builtins/isinf_sign.zig new file mode 100644 index 0000000..532f267 --- /dev/null +++ b/src/builtins/isinf_sign.zig @@ -0,0 +1,5 @@ +/// Similar to isinf, except the return value is -1 for an argument of -Inf and 1 for an argument of +Inf. +pub inline fn __builtin_isinf_sign(x: anytype) c_int { + if (!@import("std").math.isInf(x)) return 0; + return if (@import("std").math.isPositiveInf(x)) 1 else -1; +} diff --git a/src/builtins/isnan.zig b/src/builtins/isnan.zig new file mode 100644 index 0000000..e1658d4 --- /dev/null +++ b/src/builtins/isnan.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_isnan(x: anytype) c_int { + return @intFromBool(@import("std").math.isNan(x)); +} diff --git a/src/builtins/labs.zig b/src/builtins/labs.zig new file mode 100644 index 0000000..20eea56 --- /dev/null +++ b/src/builtins/labs.zig @@ -0,0 +1,4 @@ +/// Standard C Library bug: The absolute value of the most negative integer remains negative. +pub inline fn __builtin_labs(val: c_long) c_long { + return if (val == @import("std").math.minInt(c_long)) val else @intCast(@abs(val)); +} diff --git a/src/builtins/llabs.zig b/src/builtins/llabs.zig new file mode 100644 index 0000000..21f7803 --- /dev/null +++ b/src/builtins/llabs.zig @@ -0,0 +1,4 @@ +/// Standard C Library bug: The absolute value of the most negative integer remains negative. +pub inline fn __builtin_llabs(val: c_longlong) c_longlong { + return if (val == @import("std").math.minInt(c_longlong)) val else @intCast(@abs(val)); +} diff --git a/src/builtins/memcpy.zig b/src/builtins/memcpy.zig new file mode 100644 index 0000000..3202e5e --- /dev/null +++ b/src/builtins/memcpy.zig @@ -0,0 +1,11 @@ +pub inline fn __builtin_memcpy( + noalias dst: ?*anyopaque, + noalias src: ?*const anyopaque, + len: usize, +) ?*anyopaque { + if (len > 0) @memcpy( + @as([*]u8, @ptrCast(dst.?))[0..len], + @as([*]const u8, @ptrCast(src.?)), + ); + return dst; +} diff --git a/src/builtins/memcpy_chk.zig b/src/builtins/memcpy_chk.zig new file mode 100644 index 0000000..e85ac6c --- /dev/null +++ b/src/builtins/memcpy_chk.zig @@ -0,0 +1,13 @@ +pub inline fn __builtin___memcpy_chk( + noalias dst: ?*anyopaque, + noalias src: ?*const anyopaque, + len: usize, + remaining: usize, +) ?*anyopaque { + if (len > remaining) @panic("__builtin___memcpy_chk called with len > remaining"); + if (len > 0) @memcpy( + @as([*]u8, @ptrCast(dst.?))[0..len], + @as([*]const u8, @ptrCast(src.?)), + ); + return dst; +} diff --git a/src/builtins/memset.zig b/src/builtins/memset.zig new file mode 100644 index 0000000..a718868 --- /dev/null +++ b/src/builtins/memset.zig @@ -0,0 +1,5 @@ +pub inline fn __builtin_memset(dst: ?*anyopaque, val: c_int, len: usize) ?*anyopaque { + const dst_cast = @as([*c]u8, @ptrCast(dst)); + @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val))))); + return dst; +} diff --git a/src/builtins/memset_chk.zig b/src/builtins/memset_chk.zig new file mode 100644 index 0000000..ef684d3 --- /dev/null +++ b/src/builtins/memset_chk.zig @@ -0,0 +1,11 @@ +pub inline fn __builtin___memset_chk( + dst: ?*anyopaque, + val: c_int, + len: usize, + remaining: usize, +) ?*anyopaque { + if (len > remaining) @panic("__builtin___memset_chk called with len > remaining"); + const dst_cast = @as([*c]u8, @ptrCast(dst)); + @memset(dst_cast[0..len], @as(u8, @bitCast(@as(i8, @truncate(val))))); + return dst; +} diff --git a/src/builtins/mul_overflow.zig b/src/builtins/mul_overflow.zig new file mode 100644 index 0000000..91a62c4 --- /dev/null +++ b/src/builtins/mul_overflow.zig @@ -0,0 +1,5 @@ +pub fn __builtin_mul_overflow(a: anytype, b: anytype, result: *@TypeOf(a, b)) c_int { + const res = @mulWithOverflow(a, b); + result.* = res[0]; + return res[1]; +} diff --git a/src/builtins/nanf.zig b/src/builtins/nanf.zig new file mode 100644 index 0000000..7309ea6 --- /dev/null +++ b/src/builtins/nanf.zig @@ -0,0 +1,20 @@ +/// returns a quiet NaN. Quiet NaNs have many representations; tagp is used to select one in an +/// implementation-defined way. +/// This implementation is based on the description for __builtin_nan provided in the GCC docs at +/// https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fnan +/// Comment is reproduced below: +/// Since ISO C99 defines this function in terms of strtod, which we do not implement, a description +/// of the parsing is in order. +/// The string is parsed as by strtol; that is, the base is recognized by leading ‘0’ or ‘0x’ prefixes. +/// The number parsed is placed in the significand such that the least significant bit of the number is +/// at the least significant bit of the significand. +/// The number is truncated to fit the significand field provided. +/// The significand is forced to be a quiet NaN. +/// +/// If tagp contains any non-numeric characters, the function returns a NaN whose significand is zero. +/// If tagp is empty, the function returns a NaN whose significand is zero. +pub inline fn __builtin_nanf(tagp: []const u8) f32 { + const parsed = @import("std").fmt.parseUnsigned(c_ulong, tagp, 0) catch 0; + const bits: u23 = @truncate(parsed); // single-precision float trailing significand is 23 bits + return @bitCast(@as(u32, bits) | @as(u32, @bitCast(@import("std").math.nan(f32)))); +} diff --git a/src/builtins/object_size.zig b/src/builtins/object_size.zig new file mode 100644 index 0000000..6880036 --- /dev/null +++ b/src/builtins/object_size.zig @@ -0,0 +1,10 @@ +pub inline fn __builtin_object_size(ptr: ?*const anyopaque, ty: c_int) usize { + _ = ptr; + // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html + // If it is not possible to determine which objects ptr points to at compile time, + // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0 + // for type 2 or 3. + if (ty == 0 or ty == 1) return @as(usize, @bitCast(-@as(isize, 1))); + if (ty == 2 or ty == 3) return 0; + unreachable; +} diff --git a/src/builtins/popcount.zig b/src/builtins/popcount.zig new file mode 100644 index 0000000..ecc8e4b --- /dev/null +++ b/src/builtins/popcount.zig @@ -0,0 +1,5 @@ +/// popcount of a c_uint will never exceed the capacity of a c_int +pub inline fn __builtin_popcount(val: c_uint) c_int { + @setRuntimeSafety(false); + return @as(c_int, @bitCast(@as(c_uint, @popCount(val)))); +} diff --git a/src/builtins/signbit.zig b/src/builtins/signbit.zig new file mode 100644 index 0000000..b87590d --- /dev/null +++ b/src/builtins/signbit.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_signbit(val: f64) c_int { + return @intFromBool(@import("std").math.signbit(val)); +} diff --git a/src/builtins/signbitf.zig b/src/builtins/signbitf.zig new file mode 100644 index 0000000..0810fb7 --- /dev/null +++ b/src/builtins/signbitf.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_signbitf(val: f32) c_int { + return @intFromBool(@import("std").math.signbit(val)); +} diff --git a/src/builtins/strcmp.zig b/src/builtins/strcmp.zig new file mode 100644 index 0000000..14bd29f --- /dev/null +++ b/src/builtins/strcmp.zig @@ -0,0 +1,7 @@ +pub inline fn __builtin_strcmp(s1: [*c]const u8, s2: [*c]const u8) c_int { + return switch (@import("std").mem.orderZ(u8, s1, s2)) { + .lt => -1, + .eq => 0, + .gt => 1, + }; +} diff --git a/src/builtins/strlen.zig b/src/builtins/strlen.zig new file mode 100644 index 0000000..afcd025 --- /dev/null +++ b/src/builtins/strlen.zig @@ -0,0 +1,3 @@ +pub inline fn __builtin_strlen(s: [*c]const u8) usize { + return @import("std").mem.sliceTo(s, 0).len; +} diff --git a/src/main.zig b/src/main.zig index 97e5911..dad7827 100644 --- a/src/main.zig +++ b/src/main.zig @@ -86,15 +86,8 @@ fn translate(d: *aro.Driver, args: []const []const u8) !void { return; } - // TODO this should probably pass an arraylist for the generated source. - var zig_tree = try Translator.translate(gpa, d.comp, c_tree); - defer { - gpa.free(zig_tree.source); - zig_tree.deinit(gpa); - } - - const formatted = try zig_tree.render(gpa); - defer gpa.free(formatted); + const rendered_zig = try Translator.translate(gpa, d.comp, c_tree); + defer gpa.free(rendered_zig); var close_out_file = false; var out_file_path: []const u8 = ""; @@ -109,7 +102,7 @@ fn translate(d: *aro.Driver, args: []const []const u8) !void { close_out_file = true; out_file_path = path; } - out_file.writeAll(formatted) catch |err| + out_file.writeAll(rendered_zig) catch |err| return d.fatal("failed to write result to '{s}': {s}", .{ out_file_path, aro.Driver.errorDescription(err) }); if (fast_exit) process.exit(0); diff --git a/test/cases/translate/call_builtin_popcount.c b/test/cases/translate/call_builtin_popcount.c new file mode 100644 index 0000000..6018029 --- /dev/null +++ b/test/cases/translate/call_builtin_popcount.c @@ -0,0 +1,15 @@ +int max(int a) { + return __builtin_popcount(1); +} + +// translate +// +// pub inline fn __builtin_popcount(val: c_uint) c_int { +// @setRuntimeSafety(false); +// return @as(c_int, @bitCast(@as(c_uint, @popCount(val)))); +// } +// pub export fn max(arg_a: c_int) c_int { +// var a = arg_a; +// _ = &a; +// return __builtin_popcount(@as(c_uint, 1)); +// }