diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 70ff1daf40..5f11076abb 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -575,6 +575,45 @@ fn transStmt( } } +fn transCreateNodeShiftOp( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangBinaryOperator, + comptime op: ast.Node.InfixOp.Op, + comptime op_tok_id: std.zig.Token.Id, + comptime bytes: []const u8, +) !*ast.Node { + if (!(op == .BitShiftLeft or op == .BitShiftRight)) { + @compileError("op must be either .BitShiftLeft or .BitShiftRight"); + } + + const lhs_expr = ZigClangBinaryOperator_getLHS(stmt); + const rhs_expr = ZigClangBinaryOperator_getRHS(stmt); + const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr); + // lhs >> u5(rh) + + const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); + const op_token = try appendToken(rp.c, op_tok_id, bytes); + + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location); + try as_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExpr(rp, scope, rhs_expr, .used, .l_value); + try as_node.params.push(rhs.node); + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const node = try rp.c.a().create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ + .op_token = op_token, + .lhs = lhs.node, + .op = op, + .rhs = &as_node.base, + }; + + return &node.base; +} + fn transBinaryOperator( rp: RestorePoint, scope: *Scope, @@ -679,15 +718,22 @@ fn transBinaryOperator( }); } }, - .Shl, - .Shr, - => return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangBinaryOperator_getBeginLoc(stmt), - "TODO: handle more C binary operators: {}", - .{op}, - ), + .Shl => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<"); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .Shr => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>"); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, .LT => { const node = try transCreateNodeInfixOp(rp, scope, stmt, .LessThan, .AngleBracketLeft, "<", true); return maybeSuppressResult(rp, scope, result_used, TransResult{ @@ -1664,6 +1710,95 @@ fn qualTypeIsPtr(qt: ZigClangQualType) bool { return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer; } +fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 { + const ty = ZigClangQualType_getTypePtr(qt); + + switch (ZigClangType_getTypeClass(ty)) { + .Builtin => { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + + switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Char_U, + .UChar, + .Char_S, + .SChar, + => return 8, + .UInt128, + .Int128, + => return 128, + else => return 0, + } + + unreachable; + }, + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + const type_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl))); + + if (std.mem.eql(u8, type_name, "uint8_t") or std.mem.eql(u8, type_name, "int8_t")) { + return 8; + } else if (std.mem.eql(u8, type_name, "uint16_t") or std.mem.eql(u8, type_name, "int16_t")) { + return 16; + } else if (std.mem.eql(u8, type_name, "uint32_t") or std.mem.eql(u8, type_name, "int32_t")) { + return 32; + } else if (std.mem.eql(u8, type_name, "uint64_t") or std.mem.eql(u8, type_name, "int64_t")) { + return 64; + } else { + return 0; + } + }, + else => return 0, + } + + unreachable; +} + +fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node { + const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc); + + if (int_bit_width != 0) { + // we can perform the log2 now. + const cast_bit_width = std.math.log2_int(u64, int_bit_width); + const node = try rp.c.a().create(ast.Node.IntegerLiteral); + node.* = ast.Node.IntegerLiteral{ + .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}), + }; + return &node.base; + } + + const zig_type_node = try transQualType(rp, qt, source_loc); + + // @import("std").math.Log2Int(c_long); + // + // FnCall + // FieldAccess + // FieldAccess + // FnCall (.builtin = true) + // Symbol "import" + // StringLiteral "std" + // Symbol "math" + // Symbol "Log2Int" + // Symbol (var from above) + + const import_fn_call = try transCreateNodeBuiltinFnCall(rp.c, "@import"); + const std_token = try appendToken(rp.c, .StringLiteral, "\"std\""); + const std_node = try rp.c.a().create(ast.Node.StringLiteral); + std_node.* = ast.Node.StringLiteral{ + .token = std_token, + }; + try import_fn_call.params.push(&std_node.base); + import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math"); + const outer_field_access = try transCreateNodeFieldAccess(rp.c, &inner_field_access.base, "Log2Int"); + const log2int_fn_call = try transCreateNodeFnCall(rp.c, &outer_field_access.base); + try @ptrCast(*ast.Node.SuffixOp.Op.Call, &log2int_fn_call.op).params.push(zig_type_node); + log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")"); + + return &log2int_fn_call.base; +} + fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool { const ty = ZigClangQualType_getTypePtr(qt); @@ -1827,7 +1962,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.SuffixOp); node.* = ast.Node.SuffixOp{ - .lhs = fn_expr, + .lhs = .{ .node = fn_expr }, .op = ast.Node.SuffixOp.Op{ .Call = ast.Node.SuffixOp.Op.Call{ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), @@ -1839,6 +1974,17 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { return node; } +fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node.InfixOp { + const field_access_node = try c.a().create(ast.Node.InfixOp); + field_access_node.* = .{ + .op_token = try appendToken(c, .Period, "."), + .lhs = container, + .op = .Period, + .rhs = try transCreateNodeIdentifier(c, field_name), + }; + return field_access_node; +} + fn transCreateNodePrefixOp( c: *Context, op: ast.Node.PrefixOp.Op, diff --git a/test/translate_c.zig b/test/translate_c.zig index 36cdc23915..f09c1832dd 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1023,6 +1023,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("shift right with a fixed size type, no while", // TODO can fold this into "shift right assign with a fixed size type" once `while` and `>>=` and `uint32_t` are handled in translate-c-2 + \\#include + \\uint32_t some_func(uint32_t a) { + \\ uint32_t b = a >> 1; + \\ return b; + \\} + , &[_][]const u8{ + \\pub export fn some_func(a: uint32_t) uint32_t { + \\ var b: uint32_t = a >> @as(u5, 1); + \\ return b; + \\} + }); + cases.add("anonymous enum", \\enum { \\ One, @@ -1199,6 +1212,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("bitshift, no parens", // TODO can fold this into "bitshift" once parens are preserved correctly in translate-c-2 + \\int foo(void) { + \\ int a = (1 << 2); + \\ return a >> 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = 1 << @as(@import("std").math.Log2Int(c_int), 2); + \\ return a >> @as(@import("std").math.Log2Int(c_int), 1); + \\} + }); + cases.addC("compound assignment operators", \\void foo(void) { \\ int a = 0;