Commit acff2d407b

Merlyn Morgan-Graham <kavika@gmail.com>
2019-12-16 00:45:46
Add bit shift binary ops in translate-c-2
1 parent c8c8964
Changed files (2)
src-self-hosted
test
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 <zig_type_node> (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,
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 <stdint.h>
+        \\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;